---
title: "Execute a workflow"
method: POST
path: "/api/v1/workflows/execute"
tags: ["Workflows"]
---

# Execute a workflow

`POST /api/v1/workflows/execute`

## Request

- Base URL: `https://orchestrator.getaxonflow.com`
- URL: `https://orchestrator.getaxonflow.com/api/v1/workflows/execute`
- Auth: none declared
- TypeScript: <https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/docs/api/v1/workflows/execute/post.ts> (a function with typed inputs; its response is checked against the schema)

Execute a defined workflow with input parameters

## Request body

- WorkflowExecuteRequest
  - `workflow` Workflow, required
    - `metadata` object, required
      - `name` string, required
      - `description` string
    - `spec` object, required
      - `steps` WorkflowStep[], required
        - `name` string, required
        - `type` 'mcp_query' | 'llm' | 'api_call' | 'transform', required
        - `connector` string
        - `query` string
        - `prompt` string
        - `dependencies` string[]
  - `input` object
  - `user` UserContext
    - `id` integer
    - `email` string
    - `role` string
    - `region` string: User's region, read by geo-based routing policies.
    - `permissions` string[]
    - `tenant_id` string
    - `org_id` string: Organisation for multi-tenant isolation, populated from the X-Org-ID header the agent stamps on the trusted hop.

## Response `200`

Workflow execution result: every step ran, each decided by the
organization's policy before it ran (#4382). The returned `id` is
where a caller first observes the `wfe_` prefix introduced in
#3442, so an integration that matches run ids on `wf_` breaks
here rather than on a later GET.

Since v11.1.0 every step is decided on every deployment, and
`AXONFLOW_HITL_ENABLED` is ignored. Before it, steps were decided
only when that variable was `true`, and a run paused by an approval
answered `200` with `status: "paused"` (or `202` when the approval
could not be created). Neither pause exists any more: a step that
requires an approval is withheld (`403`,
`approval_requires_durable_record`). A plan run in `confirm` or
`step` mode holds its steps durably.

- WorkflowExecution
  - `id` string: In-process declarative workflow-engine run identifier. Since #3442 these carry the `wfe_` prefix, not `wf_`: this engine runs a spec handed to it in the request body and appears in `workflows`, `workflow_steps` and `execution_history` nowhere at all, so it is a different thing from a governed control-plane workflow and no longer shares that workflow's prefix. No component derives meaning from a `wfe_` id; the one place in the platform that parses id prefixes is the unified-executions resolver, which dispatches on `wf_`/`wcp_`/`plan_`, and a `wfe_` id deliberately no longer matches any of them. A caller that stored one and matches on the `wf_` prefix stops matching. Ids minted before v10.0.0 keep their old prefix; no row is rewritten.
  - `workflow_name` string
  - `status` 'pending' | 'running' | 'completed' | 'failed'
  - `input` object
  - `start_time` string, date-time: Earlier revisions of this spec named this field `started_at`; the wire has always been `start_time`. Same for `end_time` below, previously misdocumented as `completed_at`.
  - `end_time` string, date-time: Omitted while the run is still executing.
  - `user_context` UserContext
    - `id` integer
    - `email` string
    - `role` string
    - `region` string: User's region, read by geo-based routing policies.
    - `permissions` string[]
    - `tenant_id` string
    - `org_id` string: Organisation for multi-tenant isolation, populated from the X-Org-ID header the agent stamps on the trusted hop.
  - `error` string: Present only when the run failed.
  - `steps` object[]
    - `name` string
    - `status` 'pending' | 'running' | 'completed' | 'failed' | 'skipped'
    - `input` object
    - `output` object
    - `start_time` string, date-time
    - `end_time` string, date-time: Omitted while the step is still executing.
    - `error` string: Present only when the step failed.
    - `process_time` string
  - `output` object

## Other responses

- `400`: Invalid request
- `401`: The authenticated tenancy scope is missing or unusable. The handler resolves the gateway-stamped `X-Org-ID` / `X-Tenant-ID` pair before anything else and refuses when either dimension is absent, blank, or the unowned-org sentinel, with a message opening `Unauthorized: an authenticated tenant scope is required` and naming the two headers the AxonFlow Agent gateway stamps. The resolved scope then overwrites the body's `user.tenant_id` / `user.org_id` and becomes the tenancy stamped on every row this call creates. A second stamped-row write guard sits at the row boundary (#3066), but it is defense in depth: the scope resolution above already guarantees both dimensions, so that guard's distinct refusal message is not reachable on this endpoint.
- `403`: Refused, with one of two bodies. A step was refused before it ran (#4382). Every step is decided by the organization's policy before it runs, on every deployment. The body is a `MultiAgentStepRefusal`: `code` is `execution_blocked` when a policy blocks the step, or `approval_requires_durable_record` when a policy requires an approval (this route keeps no durable approval record, so it withholds the step; run the plan in `confirm` or `step` mode, whose holds are durable), or `route_refused` when the organization's route rows refuse an `llm-call` step's call (#4249; `policy` is the route's reason). No later step runs, and `soft_failure_tolerance` does not absorb a refusal. Or the request body carries a cross-tenant claim: a `user.tenant_id` naming a tenancy other than the authenticated one is refused with `Forbidden: user.tenant_id does not name the authenticated tenancy`. Omitting the body field is not an error; it is overwritten from the authenticated scope.
- `413`: The request body is over 1 MiB (1,048,576 bytes), refused whole before it is decoded, in this route's `{success, error}` envelope: `error` begins `request_too_large: `. Nothing is gated, planned or run (#4249).
- `500`: Internal server error
- `503`: The orchestrator's workflow engine decides no step (its step gate is not wired), so nothing runs. A serving orchestrator wires it at boot on every deployment.

---

[API](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api.md) - [All operations](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/revisions/f408ef3ec9dd?raw)
