Skip to content
Select themeSelect language

`GET /api/workflows/runs/{run_id}` — the run (status + timestamps) plus each step's task state so the UI can render per-step progress.

GET
/api/workflows/runs/{run_id}
curl --request GET \
--url https://example.com/api/workflows/runs/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0
run_id
required
string format: uuid

Workflow run id

Media typeapplication/json

A workflow run plus its per-node states — GET /api/workflows/runs/{run_id}.

steps keeps the per-node step_key/status/task_id list the web run view consumes; nodes is the typed per-node run view so the UI can show gate branching. Both are built from the workflow run’s child runs.

object
error_message

#1052: the run’s own failure reason — the string the engine composes from the nodes that actually CAUSED the failed verdict at terminalization (failure_reason::compose_failure_reason, shape Node '<title>' (<key>) failed: <error> (+N more nodes failed)), and the very field the #838 workflow-failure alert reads. A client that reports an outcome — the web terminal’s workflow trigger watch — can therefore state the SAME substance as the chat notification instead of re-deriving a lookalike from nodes, which cannot see the on_failure: Stop filter that decides which failures are causes. null for a run that is not failed, or whose reason could not be derived. Additive and nullable, so an older client is unaffected.

string | null
finished_at
string | null format: date-time
id
required
string format: uuid
nodes
required
Array<object>

One node’s typed run-state (ADR 0020 V2/V3) — kind and fired_handle let the UI render which branch a gate or human node took; config carries a human node’s { title, instructions } approval prompt.

#951 D1: the projection below it computes attempt, deadline_at, iteration_index, the child run_id, the start/finish timestamps and the failure diagnostic; this DTO used to drop all seven on the floor, which left the attempt chip showing a hard-coded 1, and the HITL countdown, the loop-progress badge and any per-node duration display as unreachable code. Every field here is additive and nullable, so an older client is unaffected.

object
attempt
required

1-indexed retry attempt on the node’s child run; 1 when never retried.

integer format: int32
config
required
deadline_at

A waiting human node’s auto-rejection deadline; null otherwise.

string | null format: date-time
error_message

The child run’s failure diagnostic, when it failed.

string | null
finished_at
string | null format: date-time
fired_handle
string | null
iteration_index

null on an ordinary node row and on a loop FRAME row; set on the per-iteration ANCHOR rows appended after the node block (WP V16).

integer | null format: int32
kind
required

The kind of a workflow node.

Doubles as the OpenAPI spec mirror for the closed kind string on the workflow node/step + run-node DTOs: StepResponse.kind / NodeResponse.kind narrow to this via #[schema(value_type = NodeKindSpec)]. The variant set is the workflow_nodes.kind CHECK (migration 157); the serde snake_case rename matches as_db_str one-for-one, so the spec enum equals what the handlers serialize.

string
Allowed values: agent gate human end code notify_telegram notify_email notify_webhook notify_discord loop db_query db_execute http_request transform wait_event llm_classify imap_ack app connector ssh_exec
node_key
required
string
result
required
run_id

The child runs row backing this node; null while the node is pending (the engine has not opened a run for it yet).

string | null format: uuid
started_at
string | null format: date-time
state
required
string
task_id
string | null format: uuid
started_at
string | null format: date-time
status
required
string
steps
required
Array<object>

The per-node run state of a run — what the UI renders for progress. step_key is the node’s key; task_id is the materialized step-task for an agent node (null for gate/human/end nodes).

object
status
required
string
step_key
required
string
task_id
string | null format: uuid
workflow_id
required
string format: uuid
Example
{
"nodes": [
{
"kind": "agent"
}
]
}

Structured client error

Media typeapplication/json

The canonical JSON body of every error response — the single source of truth the frontend binds to. Every AppError serializes as this exact shape, and the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is what the frontend error schema is generated from, so there is no hand-written error schema on either end.

object
code
required

Machine-readable, stable error code.

string
Allowed values: not_found unauthorized forbidden license_required license_expired bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited too_many_requests quota_exceeded database_error docker_error vault_error internal_error
details
One of:
null
error
required

Human-readable message (the server’s English text; the client may localize by code).

string
Example
{
"code": "not_found"
}

Authentication required

Media typeapplication/json

The canonical JSON body of every error response — the single source of truth the frontend binds to. Every AppError serializes as this exact shape, and the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is what the frontend error schema is generated from, so there is no hand-written error schema on either end.

object
code
required

Machine-readable, stable error code.

string
Allowed values: not_found unauthorized forbidden license_required license_expired bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited too_many_requests quota_exceeded database_error docker_error vault_error internal_error
details
One of:
null
error
required

Human-readable message (the server’s English text; the client may localize by code).

string
Example
{
"code": "not_found"
}

Permission denied

Media typeapplication/json

The canonical JSON body of every error response — the single source of truth the frontend binds to. Every AppError serializes as this exact shape, and the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is what the frontend error schema is generated from, so there is no hand-written error schema on either end.

object
code
required

Machine-readable, stable error code.

string
Allowed values: not_found unauthorized forbidden license_required license_expired bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited too_many_requests quota_exceeded database_error docker_error vault_error internal_error
details
One of:
null
error
required

Human-readable message (the server’s English text; the client may localize by code).

string
Example
{
"code": "not_found"
}

Run not found

Media typeapplication/json

The canonical JSON body of every error response — the single source of truth the frontend binds to. Every AppError serializes as this exact shape, and the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is what the frontend error schema is generated from, so there is no hand-written error schema on either end.

object
code
required

Machine-readable, stable error code.

string
Allowed values: not_found unauthorized forbidden license_required license_expired bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited too_many_requests quota_exceeded database_error docker_error vault_error internal_error
details
One of:
null
error
required

Human-readable message (the server’s English text; the client may localize by code).

string
Example
{
"code": "not_found"
}

Structured server error

Media typeapplication/json

The canonical JSON body of every error response — the single source of truth the frontend binds to. Every AppError serializes as this exact shape, and the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is what the frontend error schema is generated from, so there is no hand-written error schema on either end.

object
code
required

Machine-readable, stable error code.

string
Allowed values: not_found unauthorized forbidden license_required license_expired bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited too_many_requests quota_exceeded database_error docker_error vault_error internal_error
details
One of:
null
error
required

Human-readable message (the server’s English text; the client may localize by code).

string
Example
{
"code": "not_found"
}