ApprovalResponse
Rich response returned by the WCP `/approve` and `/reject` endpoints and by the MAP plan-scoped equivalents (`/api/v1/plans/{id}/steps/{step_id}/approve|reject`). Both planes project through the same helper — see ADR-046 (HITL response parity) and ADR-045 (retry_context wire contract). A refused approval answers the same status on both planes: 409 when the approval has expired (`approval_expired`), 503 when its state cannot be read (`approval_state_unreadable`). `decision` resolves to `allow` on a successful approval (the step can now proceed) or `block` on rejection (workflow aborted). `plan_id` is populated only on MAP-plane responses; on WCP-plane responses it is omitted. `retry_context` is always present and mirrors the StepGate `retry_context` shape.
Properties
| Name | Type | Description |
|---|---|---|
| workflow_id | string | Underlying WCP workflow identifier |
| plan_id | string | MAP plan id — present on MAP-plane responses. Omitted on WCP-plane responses (WCP has no plan concept). |
| step_id | string | Step that was approved or rejected |
| status | string | Flat string alias of `approval_status`. Both fields always carry the same value — `status` is convenient for loggers, dashboards, and clients that prefer a simple string; `approval_status` is the type |
| decision | string | Post-approval decision. Approved `require_approval` steps resolve to `allow`; rejected to `block`. `require_approval` on an approve/reject response means the step is still pending. |
| reason | string | Decision reason text. Approved / rejected responses prefix the original policy reason with `Approved:` or `Rejected:`. |
| approval_status | string | Terminal approval status after the mutation landed. `expired` is an auto-timeout (Evaluation-tier) — a terminal not-approved state that blocks the step, kept distinct from a human `rejected`. |
| approval_id | string | HITL queue entry UUID of the hold this decision acted on: the step's pending hold, else its newest (hold 1 is UUID v5 over `workflow_id + ":" + step_id`; hold n >= 2, a step held again after an earlie |
| approved_by | string | Identity (X-User-ID, typically email) that approved the step |
| approved_at | string | Timestamp when the approval was persisted |
| rejected_by | string | Identity that rejected the step (rejection path only) |
| rejected_at | string | Timestamp when the rejection was persisted |
| policies_matched | array | Policies that triggered the original `require_approval` decision |
| retry_context | object | |
| message | string | Human-readable status summary |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/api-evangelist/axonflow/main/json-schema/axonflow-approval-response-schema.json",
"title": "ApprovalResponse",
"description": "Rich response returned by the WCP `/approve` and `/reject` endpoints\nand by the MAP plan-scoped equivalents\n(`/api/v1/plans/{id}/steps/{step_id}/approve|reject`). Both planes\nproject through the same helper — see ADR-046 (HITL response parity)\nand ADR-045 (retry_context wire contract). A refused approval answers\nthe same status on both planes: 409 when the approval has expired\n(`approval_expired`), 503 when its state cannot be read\n(`approval_state_unreadable`).\n\n`decision` resolves to `allow` on a successful approval (the step can\nnow proceed) or `block` on rejection (workflow aborted). `plan_id` is\npopulated only on MAP-plane responses; on WCP-plane responses it is\nomitted. `retry_context` is always present and mirrors the StepGate\n`retry_context` shape.\n",
"x-generated": "2026-10-09",
"x-method": "derived",
"x-generator": "derive-json-schema.py",
"x-source": "openapi/axonflow-orchestrator-openapi.yml#/components/schemas/ApprovalResponse",
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Underlying WCP workflow identifier"
},
"plan_id": {
"type": "string",
"description": "MAP plan id — present on MAP-plane responses. Omitted on WCP-plane\nresponses (WCP has no plan concept).\n"
},
"step_id": {
"type": "string",
"description": "Step that was approved or rejected"
},
"status": {
"type": "string",
"enum": [
"pending",
"approved",
"rejected",
"expired"
],
"description": "Flat string alias of `approval_status`. Both fields always carry\nthe same value — `status` is convenient for loggers, dashboards,\nand clients that prefer a simple string; `approval_status` is the\ntyped source of truth. First-class on both the WCP and MAP\nresponse shapes so existing clients reading either field keep\nworking without branching.\n"
},
"decision": {
"type": "string",
"enum": [
"allow",
"block",
"require_approval"
],
"description": "Post-approval decision. Approved `require_approval` steps resolve\nto `allow`; rejected to `block`. `require_approval` on an\napprove/reject response means the step is still pending.\n"
},
"reason": {
"type": "string",
"description": "Decision reason text. Approved / rejected responses prefix the\noriginal policy reason with `Approved:` or `Rejected:`.\n"
},
"approval_status": {
"type": "string",
"enum": [
"pending",
"approved",
"rejected",
"expired"
],
"description": "Terminal approval status after the mutation landed. `expired` is an\nauto-timeout (Evaluation-tier) — a terminal not-approved state that\nblocks the step, kept distinct from a human `rejected`.\n"
},
"approval_id": {
"type": "string",
"format": "uuid",
"description": "HITL queue entry UUID of the hold this decision acted on: the\nstep's pending hold, else its newest (hold 1 is UUID v5 over\n`workflow_id + \":\" + step_id`; hold n >= 2, a step held again\nafter an earlier hold was decided, is UUID v5 over\n`workflow_id + \":\" + step_id + \"#\" + n`). Matches the queue row\nwritten by the WCP HITL adapter. With no hold for the step (a\nstep-gate row outside its hold ids is not a hold) it is the hold-1\nid; empty on the legacy in-memory MAP flow, and omitted when the\nqueue could not be read.\n"
},
"approved_by": {
"type": "string",
"description": "Identity (X-User-ID, typically email) that approved the step"
},
"approved_at": {
"type": "string",
"format": "date-time",
"description": "Timestamp when the approval was persisted"
},
"rejected_by": {
"type": "string",
"description": "Identity that rejected the step (rejection path only)"
},
"rejected_at": {
"type": "string",
"format": "date-time",
"description": "Timestamp when the rejection was persisted"
},
"policies_matched": {
"type": "array",
"description": "Policies that triggered the original `require_approval` decision",
"items": {
"$ref": "#/$defs/PolicyMatch"
}
},
"retry_context": {
"$ref": "#/$defs/RetryContext"
},
"message": {
"type": "string",
"description": "Human-readable status summary"
}
},
"$defs": {
"PolicyMatch": {
"type": "object",
"description": "Details of a policy match during evaluation (Issue",
"properties": {
"policy_id": {
"type": "string",
"description": "Unique identifier for the policy"
},
"policy_name": {
"type": "string",
"description": "Human-readable name of the policy"
},
"action": {
"type": "string",
"description": "Action taken by this policy",
"enum": [
"allow",
"block",
"require_approval",
"redact"
]
},
"reason": {
"type": "string",
"description": "Reason for the policy match"
}
}
},
"RetryContext": {
"type": "object",
"description": "First-class retry and execution state (Issue #1673 Phase 1). Always\npresent on every `StepGateResponse`, including the first gate call.\nReplaces the ambiguous `cached: bool` signal with unambiguous state\nthe agent and policy engine can reason about.\n",
"required": [
"gate_count",
"completion_count",
"prior_completion_status",
"prior_output_available",
"prior_output",
"prior_completion_at",
"first_attempt_at",
"last_attempt_at",
"last_decision",
"idempotency_key"
],
"properties": {
"gate_count": {
"type": "integer",
"minimum": 1,
"description": "Number of /gate calls for this (workflow_id, step_id), including\nthe current call. First call returns 1.\n"
},
"completion_count": {
"type": "integer",
"minimum": 0,
"description": "Number of /complete calls for this (workflow_id, step_id). Normally\n0 on first gate, 1 after the step completes.\n"
},
"prior_completion_status": {
"type": "string",
"enum": [
"none",
"completed",
"gated_not_completed"
],
"description": "\"none\" on first gate call. \"completed\" when a prior /gate + /complete\nboth landed. \"gated_not_completed\" when a prior /gate landed but no\n/complete followed — uncertain territory the agent needs to reconcile\nagainst the downstream system before re-executing.\n"
},
"prior_output_available": {
"type": "boolean",
"description": "True iff prior_completion_status == \"completed\". Mirrors whether\nprior_output *could* be returned if include_prior_output=true.\n"
},
"prior_output": {
"type": [
"object",
"null"
],
"additionalProperties": true,
"description": "Always present in the schema. Populated only when the caller set\n?include_prior_output=true AND prior_output_available is true.\nOtherwise null.\n"
},
"prior_completion_at": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "Timestamp of the prior /complete call, if any."
},
"first_attempt_at": {
"type": "string",
"format": "date-time",
"description": "Timestamp of the first /gate call for this step. On the first call,\nequals last_attempt_at.\n"
},
"last_attempt_at": {
"type": "string",
"format": "date-time",
"description": "Timestamp of this /gate call."
},
"last_decision": {
"type": "string",
"enum": [
"allow",
"block",
"require_approval"
],
"description": "Decision of the immediately prior /gate call. On the first call\n(gate_count == 1), equals the current decision (first-call\ninvariant).\n"
},
"idempotency_key": {
"type": "string",
"description": "The caller-supplied business-level key recorded on this step\n(Issue #1673 Phase 2). Always present in the schema — empty\nstring `\"\"` if the caller never supplied one.\n"
}
}
}
}
}
Work with this as data
Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for schemas
4 MCP tools reach this
find_json_schemasBrowse and filter every JSON Schema in the catalog.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.
Call it yourself
curl for this page
curl "https://apis.io/api/v1/json-schemas/axonflow-approval-response"
curl "https://apis.io/api/v1/json-schemas?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.