📘 Public beta · Endpoints are stable; OpenAPI specs and SDKs ship monthly. See changelog →
Products
Orchestration
Executions

Executions

A single run of a workflow. Has its own inputs, its own steps[], and its own status.

Trigger

POST/api/workflows/{id-or-slug}/execute
Auth · API keyScope · executions:write

Body is your inputs — they become ${trigger.*} for templated nodes:

{
  "customerId": "cus_01HXY...",
  "externalId": "loan-app-2026-05-24-001",
  "bundle": { ... }
}

Response (202):

{
  "data": {
    "execution": {
      "id": "exe_01HXY...",
      "workflowId": "wkf_01HXY...",
      "status": "pending",
      "inputs": { ... },
      "createdAt": "..."
    }
  }
}

The execution is queued. The worker picks it up within a few hundred milliseconds. Subscribe to webhooks or poll.

Status lifecycle

pending → running → ┐
                    ├→ succeeded
                    ├→ failed
                    ├→ cancelled
                    └→ awaiting_approval (then re-enters running on approve)

Read

GET/api/executions/{id}
Auth · API keyScope · executions:read

Returns the execution + every step in execution order:

{
  "data": {
    "execution": {
      "id": "exe_01HXY...",
      "workflowId": "wkf_01HXY...",
      "workflowSlug": "indonesian-kyc-v1",
      "workflowVersion": 3,
      "status": "succeeded",
      "inputs": { ... },
      "externalId": "loan-app-2026-05-24-001",
      "steps": [
        {
          "id": "stp_01HXY...",
          "nodeId": "kyc",
          "type": "service_call",
          "status": "succeeded",
          "input": { /* fully-templated input that was sent */ },
          "output": { /* response body */ },
          "attempts": 1,
          "startedAt": "...",
          "completedAt": "...",
          "durationMs": 482
        },
        {
          "id": "stp_02HXZ...",
          "nodeId": "branch_on_ktp",
          "type": "decision",
          "status": "succeeded",
          "output": { "selected": "passed" }
        },
        {
          "id": "stp_03HXA...",
          "nodeId": "screening",
          "type": "service_call",
          "status": "succeeded",
          "output": { "screeningId": "scr_...", "status": "no_match" }
        }
      ],
      "createdAt": "...",
      "completedAt": "..."
    }
  }
}

Step fields

FieldNotes
nodeIdThe DAG node this step executed.
typeThe node type for quick filtering.
statuspending · running · succeeded · failed · awaiting_approval · cancelled.
inputFully-templated input as sent. Useful for debugging templating bugs.
outputThe step's output. For service_call, it's the parsed response body.
errorSet only when status: failed. Includes message + (truncated) downstream payload.
attemptsHow many times this step ran (counts retries).
durationMsWall-clock duration of the final attempt.

List executions

GET/api/executions
Auth · API keyScope · executions:read

Filters: workflowId · workflowSlug · status · externalId · from · to · page · limit.

Returns metadata only; fetch individual executions to get steps[].

Cancel

POST/api/executions/{id}/cancel
Auth · API keyScope · executions:write
{ "reason": "Customer withdrew application via support call #4521." }

Sets status to cancelled. In-flight service_call steps complete (no way to cancel the downstream HTTP call); subsequent nodes don't fire. Cancellations are audit-logged.

Retries (automatic)

Each service_call node has its own retry policy. Failures within retry budget are transparent — they appear as attempts: N on the step. Once budget exhausts, the step transitions to failed and the workflow's failure handling kicks in.

Default per-node failure handling:

  • The workflow status becomes failed.
  • workflow.execution.failed webhook fires with the failed step's error.
  • The execution is not rolled back — prior service_call side effects are not undone. If you need compensation, model it explicitly with a follow-up node.

You can override per-node:

{
  "id": "screening",
  "type": "service_call",
  ...,
  "onFailure": "ignore | route_to:<nodeId>"
}
  • ignore — log the failure but continue to the next node as if successful (output is null).
  • route_to:<nodeId> — jump to a recovery node (typically a webhook_emit to alert ops).

Idempotency

POST /api/workflows/{id}/execute does not currently deduplicate on a client-supplied key — every call starts a new execution. If your trigger path is retry-prone, deduplicate on your side (cache the last successful executionId for a given input hash and check before re-triggering).

A first-class idempotencyKey body field with a 24h dedup window is on the 2026 Q3 roadmap and will mirror the shape used by AI Automation's flow execute →.

Trace IDs

We propagate a X-Quantum-Trace-Id header through every service_call step. The trace ID is also recorded on the execution row. Use this to correlate orchestration runs with sibling-product audit logs during incident response.