`POST /api/workflows` — create a workflow from a JSON body. The DAG is validated by the service; an invalid DAG yields 400.
const url = 'https://example.com/api/workflows';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"name":"example","nodes":[{"config":"example","depends_on":[{"handle":"example","key":"example"}],"key":"example","kind":"example","parent_node_key":"example","position":{"x":1,"y":1}}],"project_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","steps":[{"agent_type":"example","depends_on":["example"],"key":"example","on_failure":"example","prompt":"example"}]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/api/workflows \ --header 'Content-Type: application/json' \ --data '{ "name": "example", "nodes": [ { "config": "example", "depends_on": [ { "handle": "example", "key": "example" } ], "key": "example", "kind": "example", "parent_node_key": "example", "position": { "x": 1, "y": 1 } } ], "project_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "steps": [ { "agent_type": "example", "depends_on": [ "example" ], "key": "example", "on_failure": "example", "prompt": "example" } ] }'Request Bodyrequired
Section titled “Request Bodyrequired”POST /api/workflows body — create a workflow definition in a project.
A request may carry either the v1 steps (agent-only) or the typed nodes
array; nodes wins when present.
object
One typed node in a create/update request body (ADR 0020 V8 builder). The
builder authors all four kinds; config is the kind-specific blob the spec
layer persists verbatim, position is the canvas placement.
object
The kind-specific config blob; defaults to {}.
This node’s incoming edges.
One incoming-edge declaration of a node in the typed-node wire shape — the
source node_key plus the handle it leaves the source by (out for an
agent node, true/false for a gate, approved/rejected for a human).
object
The source’s output handle; defaults to out.
agent / gate / human / end / code / loop / notify_*.
WP V16: when set, this node lives INSIDE the named loop frame’s
sub-graph. Omitted (None) means top-level.
A node’s canvas position in a create/update request (ADR 0020 V8).
object
One step in a create/update request body — the v1 agent-only wire shape,
kept so existing callers keep working. The V8 builder sends nodes instead.
object
stop (default) or continue; unknown values fall back to stop.
Examplegenerated
{ "name": "example", "nodes": [ { "config": "example", "depends_on": [ { "handle": "example", "key": "example" } ], "key": "example", "kind": "example", "parent_node_key": "example", "position": { "x": 1, "y": 1 } } ], "project_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "steps": [ { "agent_type": "example", "depends_on": [ "example" ], "key": "example", "on_failure": "example", "prompt": "example" } ]}Responses
Section titled “Responses”Workflow created; body contains the new workflow id
{ "id": <uuid> } — the create/import response body (the new workflow id);
the handlers return this struct directly.
object
Examplegenerated
{ "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}Structured client error
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
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Authentication required
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
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Permission denied
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
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}Structured server error
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
Machine-readable, stable error code.
Present only on a quota-exceeded 403 — the inline upgrade-CTA payload.
object
The entitlement feature key that was hit, e.g. apps.max_count.
The plan’s limit for this key.
Where to send the user to upgrade.
Current usage (count or bytes, per the key).
Human-readable message (the server’s English text; the client may localize
by code).
Example
{ "code": "not_found"}