Workflows API

Multi-agent workflow pipelines and their runs.

There is no POST /workflows or PUT /workflows/{id} — no such routes exist in routes.rs, and the Action enum has no WorkflowCreate/WorkflowUpdate variant. Workflows enter the system only via install-from-package. To provision a workflow, install a WorkflowTemplate package via POST /catalog/install.

Endpoints

MethodPathActionDescription
GET/workflowsWorkflowListList installed workflow definitions
GET/workflows/{id}WorkflowStatusGet a workflow definition
POST/workflows/{id}/runWorkflowRunStart a run
GET/workflows/{id}/runsWorkflowListList runs for one workflow
GET/runsWorkflowListList runs across every installed workflow
GET/runs/{id}WorkflowStatusGet a specific run's status
POST/workflows/stepWorkflowRunLegacy single-step execution (kept for backward compat)

WorkflowRun is admin/operator/developer/service; WorkflowList/WorkflowStatus are broadly readable (admin/operator/developer/viewer/service/auditor).


GET /workflows

curl -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  http://localhost:4200/workflows

Response (200) — WorkflowDefinition[]

{
  "workflows": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "claims-pipeline",
      "description": "End-to-end claims processing",
      "steps": [
        {
          "name": "intake",
          "agent": {"ByName": "claims-intake-agent"},
          "prompt_template": "Process this claim: {{input}}",
          "mode": "Sequential",
          "timeout_secs": 120,
          "error_mode": "Fail",
          "output_var": "claim_data"
        }
      ],
      "created_at": "2026-06-04T10:00:00Z"
    }
  ]
}

agent is {"ByName": "..."} or {"ById": "<uuid>"}. error_mode is "Fail", "Skip", or {"Retry": {"max_retries": N}}.


GET /workflows/{id}

Same shape as one entry of /workflows, plus admission_state:

{
  "id": "a1b2c3d4-...",
  "name": "claims-pipeline",
  "description": "...",
  "steps": [...],
  "created_at": "2026-06-04T10:00:00Z",
  "admission_state": "active"
}

admission_state is "active" (accepting new runs), "draining" (rejecting new runs, in-flight runs continue — entered by a suspend not exposed on this page), or "suspended" (no runs in flight, none admitted). null if the workflow has no admission-state record.

Error (400 — invalid ID)

{"error": {"category": "ValidationError", "message": "Invalid workflow ID format"}}

POST /workflows/{id}/run

curl -X POST -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"input": "New auto claim: rear-end collision on I-95, policy POL-2024-200"}' \
  http://localhost:4200/workflows/a1b2c3d4-.../run

Request Body

FieldTypeRequiredDescription
inputstringYesInitial input text for the workflow

Response (201) — WorkflowRunStatus

{
  "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "workflow_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "workflow_name": "claims-pipeline",
  "input": "New auto claim: rear-end collision on I-95...",
  "state": "Running",
  "step_results": [],
  "output": null,
  "error": null,
  "started_at": "2026-06-04T10:15:00Z",
  "completed_at": null
}

Note this is 201 Created, not 200. Poll GET /runs/{id} for completion.

Completed run (as later returned by GET /runs/{id})

{
  "id": "c3d4e5f6-...",
  "workflow_id": "a1b2c3d4-...",
  "workflow_name": "claims-pipeline",
  "input": "...",
  "state": "Completed",
  "step_results": [
    {
      "step_name": "intake",
      "agent_id": "550e8400-e29b-41d4-a716-446655440000",
      "agent_name": "claims-intake-agent",
      "output": "Claim data collected...",
      "tokens_used": 412,
      "duration_ms": 2300
    }
  ],
  "output": "Claim #CLM-2026-042 created. Routed to adjuster queue: auto-collision-moderate.",
  "error": null,
  "started_at": "2026-06-04T10:15:00Z",
  "completed_at": "2026-06-04T10:15:04Z"
}

Note step_results[].step_name (not step), and each entry carries agent_id/agent_name/tokens_used.


GET /workflows/{id}/runs

curl -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  "http://localhost:4200/workflows/a1b2c3d4-.../runs?state=Completed"

Query Parameters

ParamTypeDescription
statestringFilter by RunState (see below)

Response (200)

{"runs": [ /* WorkflowRunStatus[] */ ]}

GET /runs

Cross-workflow run listing (issue #634 Phase 6) — avoids fanning out /workflows/{id}/runs per installed workflow.

curl -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  "http://localhost:4200/runs?state=Running"

Same response/query shape as /workflows/{id}/runs, unfiltered by workflow.


GET /runs/{id}

curl -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  http://localhost:4200/runs/c3d4e5f6-a7b8-9012-cdef-123456789012

Full WorkflowRunStatus (same shape as the run-completion example above).


POST /workflows/step (legacy)

Executes a single agent turn directly, bypassing the workflow definition/run tracking entirely — kept for backward compatibility, not part of the current workflow model.

curl -X POST -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"target_agent": "550e8400-e29b-41d4-a716-446655440000", "input": "Process this claim"}' \
  http://localhost:4200/workflows/step

Request Body (WorkflowStep)

FieldTypeRequiredDescription
target_agentstring (UUID)YesAgent to execute the step
inputstringYesInput payload
timeoutinteger (ms)NoOptional timeout override

Response (200) — WorkflowStepResult

{"output": "...", "pii_tokenized": false, "duration": 2100}

Run States

StateDescription
PendingRun created but not yet started
RunningCurrently executing steps
SuspendedPaused, preserves position for later resume
CompletedAll steps finished successfully (terminal)
FailedAborted due to a step failure per its error_mode (terminal)
EscalatedEscalated to a human operator — outcome delivered, billable per ADR-033 (terminal)

is_terminal() is Completed | Failed | EscalatedFailed is not the only terminal outcome.


  • catalog.md — installing WorkflowTemplate packages (the only way to create a workflow)
  • agents.md — the agents each step routes to
  • usage-budget.mdCompleted/Escalated runs feed GET /usage's cost totals