AxonFlow · Schema

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.

CompanyAI GovernanceAI AgentsPolicy EnforcementAudit LoggingComplianceMCPOpen Source

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
View JSON Schema on GitHub

JSON Schema

axonflow-approval-response-schema.json Raw ↑
{
  "$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.
All 92 tools →

Call it yourself

curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/axonflow-approval-response"
All schemas
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.