Skip to content
Select themeSelect language

`PUT /api/workflows/{id}` — replace the workflow's steps (and optionally its name). A clean full-replace: the service re-validates the DAG, deletes the workflow's steps and re-inserts the request's steps. An invalid DAG -> 400.

PUT
/api/workflows/{id}
curl --request PUT \
--url https://example.com/api/workflows/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0 \
--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 } } ], "steps": [ { "agent_type": "example", "depends_on": [ "example" ], "key": "example", "on_failure": "example", "prompt": "example" } ] }'
id
required
string format: uuid

Workflow id

Media typeapplication/json

PUT /api/workflows/{id} body — full-replace the workflow’s graph. Carries either the v1 steps shape or the typed nodes array (the V8 builder).

object
name

Optional rename; the existing name is kept when omitted.

string | null
nodes
Array<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
config

The kind-specific config blob; defaults to {}.

depends_on

This node’s incoming edges.

Array<object>

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
handle

The source’s output handle; defaults to out.

string
key
required
string
key
required
string
kind
required

agent / gate / human / end / code / loop / notify_*.

string
parent_node_key

WP V16: when set, this node lives INSIDE the named loop frame’s sub-graph. Omitted (None) means top-level.

string | null
position

A node’s canvas position in a create/update request (ADR 0020 V8).

object
x
number format: double
y
number format: double
steps
Array<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
agent_type
required
string
depends_on
Array<string>
key
required
string
on_failure

stop (default) or continue; unknown values fall back to stop.

string | null
prompt
required
string
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
}
}
],
"steps": [
{
"agent_type": "example",
"depends_on": [
"example"
],
"key": "example",
"on_failure": "example",
"prompt": "example"
}
]
}
Media typeapplication/json

A workflow plus its graph — the GET/PUT /api/workflows/{id} body.

steps keeps the v1 agent-only wire shape (every agent node in ordinal order with depends_on rebuilt from the edge set) so older callers keep working. nodes + edges is the typed-node graph the V8 builder consumes: every node kind with its config, position and handle-carrying edges.

object
created_at
required
string format: date-time
edges
required
Array<object>

One directed edge as returned to the client (ADR 0020 V8) — a connection from source’s source_handle output into target.

object
id
required
string format: uuid
source
required
string
source_handle
required
string
target
required
string
id
required
string format: uuid
max_concurrent_runs

Issue #110: per-workflow concurrency cap; None = unlimited.

integer | null format: int32
name
required
string
nodes
required
Array<object>

One typed node as returned to the client (ADR 0020 V8). Carries the node’s kind, its kind-specific config blob and its canvas position — the shape the Svelte Flow builder renders directly.

object
config
required
id
required
string format: uuid
key
required
string
kind
required

The node kind (node.kind, workflow_nodes.kind CHECK). Runtime stays String; the spec narrows it to the closed [workflow_svc::NodeKindSpec].

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
ordinal
required
integer format: int32
parent_node_key

WP V16: when set, this node lives INSIDE the named loop frame’s sub-graph. The V8 builder wires up parentId / extent: 'parent' on Svelte Flow nodes carrying this field.

string | null
position
required

A node’s canvas position in a response body.

object
x
required
number format: double
y
required
number format: double
project_id
required
string format: uuid
steps
required
Array<object>

A workflow step (an agent node) as returned to the client.

ADR 0020 reshapes the schema to typed nodes + edges; this DTO keeps the v1 agent-step wire shape (step_key + depends_on) so the existing web canvas stays functional — depends_on is rebuilt from the node’s incoming edges.

object
agent_type
required
string
depends_on
required
Array<string>
id
required
string format: uuid
kind
required

The node kind (node.kind, workflow_nodes.kind CHECK). Runtime stays String; the spec narrows it to the closed [workflow_svc::NodeKindSpec].

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
on_failure
required
string
ordinal
required
integer format: int32
prompt_template
required
string
step_key
required
string
Example
{
"nodes": [
{
"kind": "agent"
}
],
"steps": [
{
"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 bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited 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 bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited 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 bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited 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"
}

Workflow 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 bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited 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 bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited 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"
}