HTTP API¶
Every operation the server exposes, generated from the OpenAPI
document it produces. The credential column is what the operation
requires: operator is the operator token (or nothing at all on a
loopback bind), task is the per-task token an agent is given.
How to authenticate, read the event stream and generate a client
is in Using the HTTP API.
| Method | Path | Tag | Operation | Credential | Summary |
|---|---|---|---|---|---|
GET |
/api/agent/tasks/{task_id} |
agent | get_task_api_agent_tasks__task_id__get |
taskToken | Read this task: its brief and the work log |
POST |
/api/agent/tasks/{task_id}/ask |
agent | ask_api_agent_tasks__task_id__ask_post |
taskToken | Ask the operator a question |
POST |
/api/agent/tasks/{task_id}/log |
agent | append_log_api_agent_tasks__task_id__log_post |
taskToken | Append this attempt's deliverable |
GET |
/api/agent/tasks/{task_id}/requests/{request_id} |
agent | poll_request_api_agent_tasks__task_id__requests__request_id__get |
taskToken | Wait for the answer to a question |
POST |
/api/agent/tasks/{task_id}/submit |
agent | submit_api_agent_tasks__task_id__submit_post |
taskToken | Submit this task's structured result |
GET |
/api/events |
events | events_api_events_get |
operatorBearer | The event stream |
GET |
/api/health |
system | health_api_health_get |
none | Liveness and version |
GET |
/api/me |
system | me_api_me_get |
none | Whether the caller needs a token, and has one |
GET |
/api/plugins |
plugins | manifest_api_plugins_get |
operatorBearer | Everything the registered workflows contribute to the UI |
POST |
/api/plugins/{wf}/actions/{name} |
plugins | run_action_api_plugins__wf__actions__name__post |
operatorBearer | Run one of a workflow's declared actions |
GET |
/api/requests |
requests | list_requests_api_requests_get |
operatorBearer | The inbox: every request still waiting on a person |
GET |
/api/requests/{request_id} |
requests | get_request_api_requests__request_id__get |
operatorBearer | One request and its answer |
POST |
/api/requests/{request_id}/answer |
requests | answer_request_api_requests__request_id__answer_post |
operatorBearer | Answer a request |
GET |
/api/runs |
runs | list_runs_api_runs_get |
operatorBearer | Every run, in dispatch order |
DELETE |
/api/runs/{run_id} |
runs | delete_run_api_runs__run_id__delete |
operatorBearer | Delete a run and everything under it |
GET |
/api/runs/{run_id} |
runs | get_run_api_runs__run_id__get |
operatorBearer | One run, its attempts and its totals |
PATCH |
/api/runs/{run_id} |
runs | edit_run_api_runs__run_id__patch |
operatorBearer | Change a run's title or description |
POST |
/api/runs/{run_id}/cancel |
runs | cancel_run_api_runs__run_id__cancel_post |
operatorBearer | End a run and everything under it |
GET |
/api/runs/{run_id}/events |
runs | get_events_api_runs__run_id__events_get |
operatorBearer | A run's stored events |
GET |
/api/runs/{run_id}/graph |
runs | get_graph_api_runs__run_id__graph_get |
operatorBearer | The workflow's graph, for this run |
GET |
/api/runs/{run_id}/log |
runs | get_log_api_runs__run_id__log_get |
operatorBearer | A run's work log |
POST |
/api/runs/{run_id}/log |
runs | append_log_api_runs__run_id__log_post |
operatorBearer | Append an operator note to the work log |
POST |
/api/runs/{run_id}/pause |
runs | pause_run_api_runs__run_id__pause_post |
operatorBearer | Stop a run dispatching |
POST |
/api/runs/{run_id}/position |
runs | move_run_api_runs__run_id__position_post |
operatorBearer | Move a run in the dispatch list |
GET |
/api/runs/{run_id}/requests |
runs | get_requests_api_runs__run_id__requests_get |
operatorBearer | A run's requests |
POST |
/api/runs/{run_id}/rerun |
runs | rerun_node_api_runs__run_id__rerun_post |
operatorBearer | Run a node again |
POST |
/api/runs/{run_id}/resume |
runs | resume_run_api_runs__run_id__resume_post |
operatorBearer | Let a paused run dispatch again |
GET |
/api/tasks/{task_id} |
tasks | get_task_api_tasks__task_id__get |
operatorBearer | One attempt and what was submitted for it |
POST |
/api/tasks/{task_id}/move |
tasks | move_task_api_tasks__task_id__move_post |
operatorBearer | Move a task's work to another node |
POST |
/api/tasks/{task_id}/retry |
tasks | retry_task_api_tasks__task_id__retry_post |
operatorBearer | Queue another attempt of a task |
POST |
/api/tasks/{task_id}/status |
tasks | set_status_api_tasks__task_id__status_post |
operatorBearer | Write a task's status |
GET |
/api/tasks/{task_id}/stream |
tasks | get_stream_api_tasks__task_id__stream_get |
operatorBearer | A page of a task's agent transcript |
GET |
/api/workflows |
workflows | list_workflows_api_workflows_get |
operatorBearer | Every workflow this server can run |
POST |
/api/workflows |
workflows | register_workflow_api_workflows_post |
operatorBearer | Register a workflow from a target |
DELETE |
/api/workflows/{name} |
workflows | remove_workflow_api_workflows__name__delete |
operatorBearer | Unregister a workflow |
GET |
/api/workflows/{name} |
workflows | get_workflow_api_workflows__name__get |
operatorBearer | One workflow |
PUT |
/api/workflows/{name} |
workflows | reload_workflow_api_workflows__name__put |
operatorBearer | Reload a registered workflow |
POST |
/api/workflows/{name}/runs |
workflows | submit_run_api_workflows__name__runs_post |
operatorBearer | Submit a run |
GET |
/api/workflows/{name}/source |
workflows | get_source_api_workflows__name__source_get |
operatorBearer | The Python a workflow is defined in |
POST |
/mcp/agent |
agent | mcp_agent |
taskToken | Model Context Protocol server (streamable HTTP) |
workflows¶
What this server can run, and how to start a run of it.
GET /api/workflows¶
Every workflow this server can run
Credential: operatorBearer. Operation id: list_workflows_api_workflows_get.
The registered workflows, by name.
What the New Run overlay's workflow chips and the library's left list are built from. A server with nothing registered answers with an empty list: it runs no workflows, which is a fact rather than a failure.
Responses
| Status | Body | Description |
|---|---|---|
200 |
array of WorkflowOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
POST /api/workflows¶
Register a workflow from a target
Credential: operatorBearer. Operation id: register_workflow_api_workflows_post.
Load target and register the workflow it names, without a restart.
The name it registers under is the loaded workflow's own. Answers 201
with the workflow as GET /api/workflows/{name} reports it; 409
conflict if that name is already registered; 422
workflow_load_failed when the load stopped — its body carries
target, stage (target, import, attribute, finalize,
plugins or register) and detail, the underlying error's full
message; 422 unknown_pool for a pool the engine does not have,
naming the ones it does; 500 persist_failed (path, detail) when
persist was asked for and the row could not be written, in which
case nothing was registered; 503 registration_unavailable from an
application built without a registrar. A response that wrote the row
carries X-Athanore-Persisted: <path>.
Request body (required): application/json, RegisterWorkflow
Responses
| Status | Body | Description |
|---|---|---|
201 |
WorkflowOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
DELETE /api/workflows/{name}¶
Unregister a workflow
Credential: operatorBearer. Operation id: remove_workflow_api_workflows__name__delete.
Drop the workflow name, interrupting whatever it was running.
Every attempt of it in flight is cancelled and left where it was — no
task status is written, and the run reads unregistered: true until
the workflow is registered again, at which point those rows are
recovered. Nothing is deleted. Answers 200 with the interrupted task
ids; 404 unknown_workflow; 500 persist_failed; 503
registration_unavailable. A response that removed the row carries
X-Athanore-Persisted: <path>; one that found no row to remove
carries no such header.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
name |
path | string |
yes | The registered workflow's name. |
persist |
query | boolean |
no | Remove the [workflows.<name>] row of athanore.toml too; a name with no row is not an error. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
RemovedWorkflowOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/workflows/{name}¶
One workflow
Credential: operatorBearer. Operation id: get_workflow_api_workflows__name__get.
One registered workflow, or 404 unknown_workflow.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
name |
path | string |
yes | The registered workflow's name. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
WorkflowOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
PUT /api/workflows/{name}¶
Reload a registered workflow
Credential: operatorBearer. Operation id: reload_workflow_api_workflows__name__put.
Load target — or the registration's recorded one — and replace name.
The next task of every run of the workflow dispatches on the new
graph; an attempt already running finishes on the body it started
with. Answers 200 with the workflow as GET /api/workflows/{name}
now reports it; 404 unknown_workflow; 409 conflict if the target
now defines a differently named workflow (that is a new workflow —
POST it) or pool would move the workflow while attempts of it are
in flight; 422 workflow_load_failed as POST answers, with stage:
"target" when target was omitted and the registration has no
recorded one; 422 unknown_pool; 500 persist_failed; 503
registration_unavailable. A response that rewrote the row carries
X-Athanore-Persisted: <path>.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
name |
path | string |
yes | The registered workflow's name. |
Request body (required): application/json, ReloadWorkflow
Responses
| Status | Body | Description |
|---|---|---|
200 |
WorkflowOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/workflows/{name}/runs¶
Submit a run
Credential: operatorBearer. Operation id: submit_run_api_workflows__name__runs_post.
Queue a run of this workflow, and return its id.
The run is queued rather than running: waiting for a slot is a
state an operator can see, and the first claim of one of its tasks is
what flips it.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
name |
path | string |
yes | The registered workflow's name. |
Request body (required): application/json, NewRun
Responses
| Status | Body | Description |
|---|---|---|
201 |
Created |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/workflows/{name}/source¶
The Python a workflow is defined in
Credential: operatorBearer. Operation id: get_source_api_workflows__name__source_get.
The module a workflow's nodes are defined in, and their line numbers.
The file is the one the start node's body is defined in — the
module the author wrote the workflow in — and nodes carries a line
for each node whose body is defined in that same file. A node whose
function was imported from elsewhere is left out rather than given a
line into text that does not contain it.
A workflow whose source Python cannot produce — a body built by
exec, or one whose file has been deleted since import — is a 404:
the workflow is registered, but this view of it does not exist.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
name |
path | string |
yes | The registered workflow's name. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
SourceOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
runs¶
A run's state, its history and the operator verbs that change it.
GET /api/runs¶
Every run, in dispatch order
Credential: operatorBearer. Operation id: list_runs_api_runs_get.
The run list of 08 §Runs, whole.
Run lists are small and return whole. The order is
the dispatch order — position ascending — so the list the
operator reads is the order the scheduler will claim in.
An application with no store has no runs and answers with an empty
list: "nothing to ask" and "nothing queued" are the same fact for a
server that holds no work, unlike /api/health's counts.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
status |
query | RunStatus | null |
no | Only runs in this status. |
workflow |
query | string | null |
no | Only runs of this workflow. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
array of RunSummary |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
DELETE /api/runs/{run_id}¶
Delete a run and everything under it
Credential: operatorBearer. Operation id: delete_run_api_runs__run_id__delete.
Cancel what is outstanding, then remove the run.
Both halves are Ops.delete's, in that order and for its reason:
the attempts are killed against rows that still exist.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Responses
| Status | Body | Description |
|---|---|---|
204 |
empty | Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/runs/{run_id}¶
One run, its attempts and its totals
Credential: operatorBearer. Operation id: get_run_api_runs__run_id__get.
A run with everything the overview pane draws.
current_nodes and pending_requests are the list query's two
derived fields, recomputed from this read: the attempts are already
in hand, so the in-flight nodes cost nothing, and the pending
requests are one indexed query over this run alone.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
RunDetail |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
PATCH /api/runs/{run_id}¶
Change a run's title or description
Credential: operatorBearer. Operation id: edit_run_api_runs__run_id__patch.
Write the fields the body names, and answer with the run.
A field the body omits is left alone, which is why an edit that
names neither is accepted and changes nothing (Ops.edit). The
response is the whole detail rather than the edited row: the SPA
re-renders the overview from it, and a second GET to get the
attempts back would be a round trip for nothing.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Request body (required): application/json, EditRun
Responses
| Status | Body | Description |
|---|---|---|
200 |
RunDetail |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/runs/{run_id}/cancel¶
End a run and everything under it
Credential: operatorBearer. Operation id: cancel_run_api_runs__run_id__cancel_post.
Cancel the run and every outstanding attempt; 409 if it has ended.
The note names how many attempts were stopped, because that is
the part of the outcome the request could not predict: a run with
nothing in flight and a run with three agents mid-turn are the same
call and very different events. It is omitted when there were none,
rather than sent as "0 attempts".
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
Ok |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/runs/{run_id}/events¶
A run's stored events
Credential: operatorBearer. Operation id: get_events_api_runs__run_id__events_get.
One page of the run's history, oldest first.
The same typed envelope the SSE feed sends, so a client
that caught up from here and then subscribed switches sources
without switching shapes. task.stream is ephemeral and is never
stored, so it never appears in a page.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
after |
query | integer |
no | Return events after this event id. |
limit |
query | integer |
no | How many events to return. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
array of RunCreatedEvent | RunStartedEvent | RunUpdatedEvent | RunReorderedEvent | RunPausedEvent | RunResumedEvent | RunCancelledEvent | RunDeletedEvent | RunCompletedEvent | RunFailedEvent | TaskEnqueuedEvent | JoinArrivedEvent | TaskStartedEvent | TaskDoneEvent | TaskFailedEvent | TaskDeadLetteredEvent | TaskWaitingEvent | TaskResumedEvent | TaskCancelledEvent | TaskMovedEvent | TaskStatusSetEvent | TaskStreamEvent | SubmissionAcceptedEvent | SubmissionRejectedEvent | SubmissionRepairEvent | RequestOpenedEvent | RequestAnsweredEvent | LogAppendedEvent | AgentStatsEvent | EngineRecoveredEvent | EngineStoppingEvent | WorkflowRegisteredEvent | WorkflowReplacedEvent | WorkflowUnregisteredEvent | PluginEvent |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/runs/{run_id}/graph¶
The workflow's graph, for this run
Credential: operatorBearer. Operation id: get_graph_api_runs__run_id__graph_get.
The registered graph with this run's history projected onto it.
A run whose workflow this server does not have registered is a 404
unknown_workflow: there is no graph to project onto, and the run
list already says so with unregistered rather than pretending a
shape.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
GraphOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/runs/{run_id}/log¶
A run's work log
Credential: operatorBearer. Operation id: get_log_api_runs__run_id__log_get.
Every entry of the work log, oldest first and never truncated.
Whole, and unpaged: the log is bounded by the number of stages
rather than by agent output. The stats entries
an agent never sees are here — token counts are operator
information.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
array of LogEntry |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/runs/{run_id}/log¶
Append an operator note to the work log
Credential: operatorBearer. Operation id: append_log_api_runs__run_id__log_post.
Write a user entry under the run's current node.
The node is the one node with an attempt in flight, and user
when there is not exactly one: a note filed under one branch of a
fan-out would claim a context it does not have (Ops.append_log).
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Request body (required): application/json, LogText
Responses
| Status | Body | Description |
|---|---|---|
200 |
LogRef |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/runs/{run_id}/pause¶
Stop a run dispatching
Credential: operatorBearer. Operation id: pause_run_api_runs__run_id__pause_post.
Pause a queued or running run; 409 on anything else.
Pause is about the next task: an attempt already in flight runs
to its end and enqueues its successor, and that successor waits (04
§Operator operations). Killing running work is /cancel.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
Ok |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/runs/{run_id}/position¶
Move a run in the dispatch list
Credential: operatorBearer. Operation id: move_run_api_runs__run_id__position_post.
Swap with a neighbour, or move to a zero-based index.
Both ends are a no-op that still answers 200 with the position the
run already had, and an index outside the list is clamped to it:
"top" is {"index": 0} and there is nothing for the caller to
bounds-check. The body model has already refused neither-or-both of
the two fields.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Request body (required): application/json, Position
Responses
| Status | Body | Description |
|---|---|---|
200 |
PositionOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/runs/{run_id}/requests¶
A run's requests
Credential: operatorBearer. Operation id: get_requests_api_runs__run_id__requests_get.
Every request the run opened, answered or not, oldest first.
The run's history rather than the inbox: a stale request — one whose
attempt is gone — stays here and leaves /api/requests?pending=true.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
array of RequestView |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/runs/{run_id}/rerun¶
Run a node again
Credential: operatorBearer. Operation id: rerun_node_api_runs__run_id__rerun_post.
Enqueue a fresh attempt of node with the payload it last had.
A join replays the arrivals the store holds rather than the payload its task was given, which is what makes a rerun the remedy for a branch that arrived late.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Request body (required): application/json, Rerun
Responses
| Status | Body | Description |
|---|---|---|
200 |
TaskRef |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/runs/{run_id}/resume¶
Let a paused run dispatch again
Credential: operatorBearer. Operation id: resume_run_api_runs__run_id__resume_post.
Resume a paused run; 409 on anything else.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
run_id |
path | string |
yes | The run id, a ULID. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
Ok |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
tasks¶
One attempt of one node: what it submitted, what it streamed, and the verbs that re-attempt it.
GET /api/tasks/{task_id}¶
One attempt and what was submitted for it
Credential: operatorBearer. Operation id: get_task_api_tasks__task_id__get.
The operator view of a task: 08's TaskRow plus submissions.
Every accepted submission, oldest first, because "last valid wins" is a rule about which one the body reads and not about which ones happened: an operator reading a repaired attempt wants the rejected shape and the accepted one.
No token, in this response or any other: a task token is
header-only, and TaskDetail has no field that
could carry one.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task id, unique across runs. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
TaskDetail |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/tasks/{task_id}/move¶
Move a task's work to another node
Credential: operatorBearer. Operation id: move_task_api_tasks__task_id__move_post.
Cancel the attempt and enqueue its payload at node.
409 conflict when node is a join: a join is
dispatched by its arrivals and called with all of them, so a task
moved into one would be a join attempt holding a single branch's
payload, with no arrival recorded and a run left waiting for
branches that already landed.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task id, unique across runs. |
Request body (required): application/json, Move
Responses
| Status | Body | Description |
|---|---|---|
200 |
TaskRef |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/tasks/{task_id}/retry¶
Queue another attempt of a task
Credential: operatorBearer. Operation id: retry_task_api_tasks__task_id__retry_post.
Enqueue the next attempt of a task that has stopped.
Same node, same payload, same branch and the same created, so a
retry keeps its place in the dispatch order (Ops.retry). Refused
with 409 conflict while the task is still going: a second attempt
of a task that already has one is two attempts of one task.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task id, unique across runs. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
TaskRef |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/tasks/{task_id}/status¶
Write a task's status
Credential: operatorBearer. Operation id: set_status_api_tasks__task_id__status_post.
Put the task in one of the three statuses an operator may write.
ready re-dispatches it and re-opens a terminal run, cancelled
stops it and dead_letter files it as failed for good; the other
four statuses are the engine's record of what happened and are not an
operator's to declare, which is why the body's field is a
Literal and a fourth value is a 422 before the engine is reached.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task id, unique across runs. |
Request body (required): application/json, SetStatus
Responses
| Status | Body | Description |
|---|---|---|
200 |
Ok |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/tasks/{task_id}/stream¶
A page of a task's agent transcript
Credential: operatorBearer. Operation id: get_stream_api_tasks__task_id__stream_get.
The chunks after after, in transcript order.
after=0 is the whole transcript from the start, which is what a
tab opening on a finished attempt asks for; a client following a
live one passes the seq_to of the ephemeral task.stream event
it just received and appends what comes back.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task id, unique across runs. |
after |
query | integer |
no | Return chunks after this sequence number. |
limit |
query | integer |
no | How many chunks to return. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
StreamOut |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
requests¶
The inbox: everything waiting on a person, and the answers.
GET /api/requests¶
The inbox: every request still waiting on a person
Credential: operatorBearer. Operation id: list_requests_api_requests_get.
The requests of every run, or of one, oldest first.
"Pending" is narrower than "unanswered": a request whose task has
ended is stale and leaves the inbox, because an answer to it
would reach nobody. It stays in the run's
own history, and pending=false here shows it too, with
stale: true on it.
An application with no store holds no requests and answers with an
empty list; a run that names no run is a filter that matches
nothing rather than a 404.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
pending |
query | boolean |
no | Only requests a person can still act on: unanswered, with the attempt that asked still running. |
run |
query | string | null |
no | Only requests of this run. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
array of RequestView |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/requests/{request_id}¶
One request and its answer
Credential: operatorBearer. Operation id: get_request_api_requests__request_id__get.
One request, answered or not, with the node that asked.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
request_id |
path | integer |
yes | The request id, unique across runs. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
RequestView |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/requests/{request_id}/answer¶
Answer a request
Credential: operatorBearer. Operation id: answer_request_api_requests__request_id__answer_post.
Record the one answer this request may have, and return it.
option_id for an options request and value for a text
or form one; the request's own mode decides which of the two
is read, so a value sent to an options request is not an
answer that request could have.
The response is the updated view rather than the answer row (08
§Requests): the SPA re-renders the card it just answered, and
pending, answer and answered_by are all part of what
changed. The author is user — this route is the operator's, and
the other author, engine, belongs to the headless fallbacks that
record an answer without a person.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
request_id |
path | integer |
yes | The request id, unique across runs. |
Request body (required): application/json, Answer
Responses
| Status | Body | Description |
|---|---|---|
200 |
RequestView |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
agent¶
What an ACP agent may call, authenticated by the task token of the attempt it is running.
GET /api/agent/tasks/{task_id}¶
Read this task: its brief and the work log
Credential: taskToken. Operation id: get_task_api_agent_tasks__task_id__get.
Everything the agent needs to start, and nothing else.
The title and the description are the run's — the brief an operator
submitted — and input is the payload this node was enqueued with,
null when it had none. log is the run's work log, oldest
first, uncapped and untruncated: it is the inter-stage channel,
it is bounded by the number of stages rather than by agent output,
and a retried attempt reads the failure entry that says why the
last one failed.
The stats entries are dropped. output_schema is here
only while a façade has declared an output_model on this
attempt's live context: the native tooling tier reads it to build
its submit_result tool, and an agent with no
model declared is not required to submit anything.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task the token was minted for. |
x-athanore-token |
header | string |
yes | The task token, from the claimed attempt. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
AgentTask |
Successful Response |
403 |
ApiError |
The task token is not valid for this task. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/agent/tasks/{task_id}/ask¶
Ask the operator a question
Credential: taskToken. Operation id: ask_api_agent_tasks__task_id__ask_post.
Open a request against this attempt, if the agent may ask at all.
403 unless the façade running this attempt declared
ask_policy="http": an agent cannot grant itself
the right to interrupt a person, and the policy lives on the agent
class the workflow chose. The prompt sections that tell an agent how
to ask are omitted when it is off, so a 403 here is an agent
that went looking.
What was sent decides the mode: options for a pick-one question,
schema for a form — whose answer is validated against that very
schema where it lands, by the validator registered here (06
§Service) — and neither for free text. The request is opened with
source="agent" and no ordinal, because a re-executed attempt does
not reproduce a turn statement for statement.
The answer does not come back here: the caller polls
/requests/{rid}, which is what lets the agent keep its turn
rather than holding this connection open.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task the token was minted for. |
x-athanore-token |
header | string |
yes | The task token, from the claimed attempt. |
Request body (required): application/json, Ask
Responses
| Status | Body | Description |
|---|---|---|
200 |
AskOut |
Successful Response |
403 |
ApiError |
The task token is not valid for this task. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/agent/tasks/{task_id}/log¶
Append this attempt's deliverable
Credential: taskToken. Operation id: append_log_api_agent_tasks__task_id__log_post.
Write one entry to the run's work log, author agent.
The deliverable of a stage, which is what the next stage reads.
It is written through the attempt's own
LogService, so it carries this
task's id and node and emits the log.appended the SPA re-renders
on — the same call a node body makes, reached over HTTP.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task the token was minted for. |
x-athanore-token |
header | string |
yes | The task token, from the claimed attempt. |
Request body (required): application/json, LogText
Responses
| Status | Body | Description |
|---|---|---|
200 |
LogRef |
Successful Response |
403 |
ApiError |
The task token is not valid for this task. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
GET /api/agent/tasks/{task_id}/requests/{request_id}¶
Wait for the answer to a question
Credential: taskToken. Operation id: poll_request_api_agent_tasks__task_id__requests__request_id__get.
The answer, waiting up to wait seconds for one to land.
wait is clamped to MAX_WAIT rather than refused, so 19's
instruction — poll with ?wait=60 and repeat until it says
answered — is a loop that costs one request per minute and not a
spin. The wait ends the moment the answer is recorded, not when the
clamp expires.
A request of another task is a 404, not someone else's answer: a token names one attempt and this route reads only what that attempt opened.
Nothing is claimed. An agent that lost a response and asked again gets the same answer again, which is what makes a dropped connection cost nothing.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task the token was minted for. |
request_id |
path | integer |
yes | A request this task opened. |
wait |
query | number |
no | Seconds to wait for an answer before reporting that there is none yet. Clamped to [0, 120]. |
x-athanore-token |
header | string |
yes | The task token, from the claimed attempt. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
AnswerPoll |
Successful Response |
403 |
ApiError |
The task token is not valid for this task. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /api/agent/tasks/{task_id}/submit¶
Submit this task's structured result
Credential: taskToken. Operation id: submit_api_agent_tasks__task_id__submit_post.
Record a value for this attempt, if it fits what was asked for.
With an output_model declared, a misfit is a 422 carrying
errors and schema and nothing is stored: the agent reads the
two in its own tool output and can fix the shape inside the same turn. The rejection is also left on the context as
ctx.last_rejection, which is what 19's repair turn quotes back if
the turn ends without a valid submission.
With none declared, any JSON is stored as it arrived.
Submitting twice is not an error: the latest valid payload wins, and a body reads it with submissions.latest(). A submission
never routes anything — the node body decides — so an agent cannot
move its own task.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
task_id |
path | integer |
yes | The task the token was minted for. |
x-athanore-token |
header | string |
yes | The task token, from the claimed attempt. |
Request body (required): application/json, any
Responses
| Status | Body | Description |
|---|---|---|
200 |
Ok |
Successful Response |
403 |
ApiError |
The task token is not valid for this task. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
POST /mcp/agent¶
Model Context Protocol server (streamable HTTP)
Credential: taskToken. Operation id: mcp_agent.
The agent surface as MCP tools: get_task, append_log, submit_result, and — when the attempt's ask_policy is http — ask_operator and wait_answer. Authenticated by the same X-Athanore-Token header as /api/agent/; the task is the token's, so no tool takes a task id. The request and response bodies are MCP's own JSON-RPC and are opaque to this document.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
X-Athanore-Token |
header | string |
yes | The task token, from the claimed attempt. |
Request body (required): application/json, any
Responses
| Status | Body | Description |
|---|---|---|
200 |
any |
One MCP response, as JSON or as an SSE stream. |
403 |
ApiError |
The task token is not valid for a live attempt. |
plugins¶
The manifest a workflow's declarations publish, and the routes and actions behind it.
GET /api/plugins¶
Everything the registered workflows contribute to the UI
Credential: operatorBearer. Operation id: manifest_api_plugins_get.
The plugin manifest: panels, actions and assets, per workflow.
Builtins first, then the workflows in registration order. The
SPA fetches this at boot, again whenever the SSE stream reconnects
onto a server with a new started_at, and on every
workflow.* event, because a manifest changes when a workflow
is registered, replaced or removed.
Responses
| Status | Body | Description |
|---|---|---|
200 |
array of PluginManifestEntry |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
POST /api/plugins/{wf}/actions/{name}¶
Run one of a workflow's declared actions
Credential: operatorBearer. Operation id: run_action_api_plugins__wf__actions__name__post.
Validate the input, resolve the scope, run the handler.
The one way an action is invoked. It answers with whatever the handler returned, as JSON.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
wf |
path | string |
yes | The workflow that declared the action. |
name |
path | string |
yes | The action's name. |
Request body (required): application/json, ActionCall
Responses
| Status | Body | Description |
|---|---|---|
200 |
any |
Successful Response |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
events¶
The live feed: one server-sent event per event of the vocabulary.
GET /api/events¶
The event stream
Credential: operatorBearer. Operation id: events_api_events_get.
Missed history, then live events, on one connection.
access_token is accepted here as a query parameter and on no other
route, because a browser's EventSource cannot set a header (08
§Authentication); the query string is never logged.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
after |
query | integer | null |
no | Replay stored events after this event id, then go live. Takes precedence over the Last-Event-ID header. |
run |
query | string | null |
no | Only events of this run. |
names |
query | string | null |
no | Comma-separated event-name globs; a * matches one dotted segment, so run.* selects run.created. Omitted means every event. |
last-event-id |
header | string | null |
no | A browser's reconnect cursor, set by EventSource. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
empty | An SSE stream. Each message is id: <event id>, event: <event name>, data: <EventEnvelope as JSON>. Ephemeral events (task.stream) carry no id:, and the resync control frame — sent when the replay overran sse_replay_cap or the server dropped events for a slow reader — carries neither an id: nor an event envelope. |
401 |
ApiError |
No operator token, on a bind that requires one. |
422 |
ApiError |
The request did not validate. code is validation and errors names each field that failed. |
system¶
Liveness and the caller's own standing. Unauthenticated, and carrying no ids.
GET /api/health¶
Liveness and version
Credential: none. Operation id: health_api_health_get.
Whether the server is up, which version it is, and what it is doing.
Pausing every run and then waiting for tasks_in_progress to reach
zero here is how an operator drains a server before stopping it.
Responses
| Status | Body | Description |
|---|---|---|
200 |
Health |
Successful Response |
GET /api/me¶
Whether the caller needs a token, and has one
Credential: none. Operation id: me_api_me_get.
Whether this server wants a token, and whether this request has one.
Never requires one itself: a request with no credential on a server
that wants one gets a 200 saying {"auth": "token", "authenticated":
false}, which is the client's cue to ask for a token.
Responses
| Status | Body | Description |
|---|---|---|
200 |
Me |
Successful Response |
Schemas¶
ActionCall¶
The body of POST /api/plugins/{wf}/actions/{name}.
input is whatever the action's form produced, and it is
validated against the action's own model on arrival rather than
described here: the model is the plugin's, so no schema this
application generates could name it ("the model
is the form"). An action that declares none takes none, and
whatever was sent is ignored.
| Field | Type | Required | Description |
|---|---|---|---|
input |
any |
no | The form's value. Validated against the action's model; a misfit is the 422 of 08 §Conventions, naming each field. |
scope |
ActionScope |
no | The ids the handler's context is resolved from. |
ActionOut¶
One action of the manifest, and the form the SPA renders for it.
| Field | Type | Required | Description |
|---|---|---|---|
confirm |
boolean |
yes | Whether the SPA asks for confirmation before calling it. |
name |
string |
yes | The action's name, as its URL spells it. |
schema |
object |
yes | JSON Schema of the input model — the form itself. |
scope |
Slot |
yes | What has to be resolved before it can run. |
title |
string |
yes | What the button and the palette entry say. |
ActionScope¶
Where an action was invoked.
The three ids a plugin route reads off its query string, sent in
the body because an action is a POST. All three are optional
here: what an action needs resolved is the scope it declared,
and mount_actions is what holds the body to it.
| Field | Type | Required | Description |
|---|---|---|---|
node |
string | null |
no | The node the action was invoked on. |
run_id |
string | null |
no | The run the action was invoked on. |
task_id |
integer | null |
no | The attempt the action was invoked on. |
AgentStats¶
The stats entry of 05 §Stats entry verbatim.
Every measurement is optional because 05 omits what it cannot determine rather than zero-filling it.
| Field | Type | Required | Description |
|---|---|---|---|
attempt |
integer |
yes | |
cost |
number | null |
no | |
denied_permissions |
integer | null |
no | |
duration_s |
number |
yes | |
input_tokens |
integer | null |
no | |
model |
string | null |
no | |
node |
string |
yes | |
output_tokens |
integer | null |
no | |
reason |
'refusal', 'cancelled', 'truncated', 'timeout', 'shutdown', 'transport', 'no_submission' | null |
no | |
repair_turns |
integer | null |
no | |
session_id |
string | null |
no | |
status |
'ok', 'failed' |
yes | |
tool_calls |
integer | null |
no | |
total_tokens |
integer | null |
no |
AgentStatsEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
AgentStats |
yes | |
id |
integer | null |
no | |
name |
'agent.stats' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
AgentTask¶
One attempt as the agent running it sees it.
The identity of the attempt, the run's title and description, the payload the node was called with, and the work log that is the inter-stage channel. Everything an agent needs to start, and nothing an operator route would add.
| Field | Type | Required | Description |
|---|---|---|---|
attempt |
integer |
yes | Which attempt of this node this is, from 1. |
description |
string |
yes | The operator's longer brief for the run. |
input |
any |
no | The payload the node was enqueued with; null when it had none. |
log |
array of LogEntry |
yes | The run's work log, oldest first and never truncated, without the stats entries. |
node |
string |
yes | The node this attempt executes. |
output_schema |
object | null |
no | The JSON Schema a submission must fit, when the agent façade running this attempt declared an output_model. |
run_id |
string |
yes | The run the attempt belongs to. |
task_id |
integer |
yes | This attempt's id; the one the token is for. |
title |
string |
yes | The operator's title for the run. |
workflow |
string |
yes | The workflow being run. |
Answer¶
Answer a request (POST /api/requests/{id}/answer).
option_id for an options request and value for a text
or form one. Both are optional here because value may
legitimately be any JSON — including null, which is why "which one
was given" cannot be decided by looking at the values alone: the
request's own mode decides, and the request service refuses the
mismatch with the error code 06 §Errors names for it.
| Field | Type | Required | Description |
|---|---|---|---|
option_id |
string | null |
no | The chosen option, for an options request. |
value |
any |
no | The answer, for a text or form request. |
AnswerAuthor¶
Who answered. engine is a headless fallback: a permission
timeout action or a declined elicitation.
'user''engine'
AnswerPoll¶
The result of one long-poll (GET …/requests/{rid}).
answered is the field to branch on. Until it is true the other
two are absent, because a request with no answer has no author
either; once it is true answer is the chosen option_id of an
options request and the value of a text or form one —
the same folding RequestView does, so
an operator reading the request and the agent reading its answer see
the same value.
Re-delivery is idempotent: nothing is claimed by a poll, so an agent that lost a response and asked again gets the same answer again.
| Field | Type | Required | Description |
|---|---|---|---|
answer |
any |
no | The chosen option_id, or the value of a text or form answer. Null before an answer, and for an answer that was null. |
answered |
boolean |
yes | Whether an answer has been recorded. |
answered_by |
AnswerAuthor | null |
no | Who answered: the operator, or the engine on a headless fallback. Null until answered. |
request_id |
integer |
yes | The request that was polled. |
ApiError¶
The body of every refusal: {error, code, ...extras}.
code is the ErrorCode vocabulary,
referenced rather than inlined, which is how the SPA gets a union
instead of a bare string. errors is the extra a
validation failure and a rejected submission carry, and it is 18's
ValidationError rather than a
second model of the same three fields: one function
(validate_submission) projects
pydantic's errors onto loc/msg/type, and the list it
returns is what the 422 body carries and what submission.rejected
publishes. The rest of 08's "...extras" are open, so the schema allows
further properties rather than pretending the two named fields are all
a body can hold.
| Field | Type | Required | Description |
|---|---|---|---|
code |
ErrorCode |
yes | The stable code a client branches on; the wording of error may change, this may not. |
error |
string |
yes | What went wrong, for a person to read. |
errors |
array of ValidationError | null |
no | Per-field detail, on a validation failure or a rejected submission. Absent otherwise. |
Arrivals¶
How much of an open fan-out has reached a join.
The innermost pending fan-out, which is the one the operator is
waiting on; the SPA renders it as 2 of 3 arrived.
| Field | Type | Required | Description |
|---|---|---|---|
arrived |
integer |
yes | Branches that have reached the join. |
count |
integer |
yes | Branches the fan-out opened. |
Ask¶
A question an agent puts to the operator (POST …/ask).
schema is spelled schema_ in Python, as it is in
RequestView, and keeps
schema as its alias — the name 08 and the curl line of 19 fix on
the wire.
| Field | Type | Required | Description |
|---|---|---|---|
options |
array of string | AskOption | null |
no | The choices, for a pick-one question. A bare string is an option that is its own id and label. |
prompt |
string |
yes | The question, as the operator will read it. |
schema |
object | null |
no | A JSON Schema the answer must fit, for a form question. |
AskOption¶
One choice an agent offers the operator.
option_id is what an answer names and what comes back from the
long-poll; name is what the operator is shown and defaults to the
id, which is what a bare string in options is short for.
| Field | Type | Required | Description |
|---|---|---|---|
kind |
string | null |
no | A label for the option; carried through as given. |
name |
string | null |
no | What to show the operator; the option_id when omitted. |
option_id |
string |
yes | The id an answer names. |
AskOut¶
The request an ask opened.
mode is reported back because the agent did not choose it
directly: it sent options, a schema or neither, and this is what that
was read as — which is also what tells it what shape of answer to
expect from the long-poll.
| Field | Type | Required | Description |
|---|---|---|---|
mode |
RequestMode |
yes | The shape of the answer expected. |
request_id |
integer |
yes | The id to poll for an answer. |
BranchRef¶
Which branch an output came out of.
The index and the key of each frame, and not the frame's fanout or
count: an output names its branch so a consumer can group by it,
and the task it came from is in the same entry for anything more.
| Field | Type | Required | Description |
|---|---|---|---|
index |
integer |
yes | The branch's zero-based index in its fan-out. |
key |
any |
no | The payload that branch was given. |
ChunkKind¶
The kinds of the agent transcript.
text and thought are the assistant's message and reasoning
chunks, tool_call/tool_result the ACP tool updates, notice
a line the façade itself wrote.
'text''thought''tool_call''tool_result''notice'
Created¶
The 201 body of POST /api/workflows/{name}/runs.
| Field | Type | Required | Description |
|---|---|---|---|
run_id |
string |
yes | The id of the run that was queued. |
EdgeKind¶
What kind of edge one node's arrow to another is.
'forward''back''join'
EditRun¶
Change a run's title, its description, or both (PATCH /api/runs/{id}).
Both fields are optional and a field that is absent is left alone —
which is why they are optional rather than defaulted: an edit that
sent no description must not blank one. An edit that names neither is
accepted and changes nothing, which is what Ops.edit does with it:
a patch is not the place to refuse a caller for being redundant.
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | null |
no | The new description, if changing. |
title |
string | null |
no | The new title, if changing. |
EngineRecovered¶
Rows reset to ready at startup. No run_id.
| Field | Type | Required | Description |
|---|---|---|---|
task_ids |
array of integer |
yes |
EngineRecoveredEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
EngineRecovered |
yes | |
id |
integer | null |
no | |
name |
'engine.recovered' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
EngineStopping¶
Attempts interrupted by a shutdown. No run_id.
| Field | Type | Required | Description |
|---|---|---|---|
task_ids |
array of integer |
yes |
EngineStoppingEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
EngineStopping |
yes | |
id |
integer | null |
no | |
name |
'engine.stopping' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
ErrorCode¶
The stable code of an error body — 08 §Conventions, in full.
A code is a contract with the SPA and the CLI: it is what a client
branches on, so it outlives any wording change to error. Adding
one is a change to 08 and to the TypeScript mirror, never to this
module alone.
'not_found''conflict''forbidden''unauthorized''validation''invalid_option''already_answered''stale_request''graph_error''unknown_workflow''unknown_node''payload_too_large''plugin_error''workflow_load_failed''unknown_pool''registration_unavailable''persist_failed'
EventName¶
Every event name in the vocabulary, one member per row of 03.
Values are subject.verb. Adding one is a change to 03 and to the
TypeScript mirror, never to this module alone.
'run.created''run.started''run.updated''run.reordered''run.paused''run.resumed''run.cancelled''run.deleted''run.completed''run.failed''task.enqueued''join.arrived''task.started''task.done''task.failed''task.dead_lettered''task.waiting''task.resumed''task.cancelled''task.moved''task.status_set''task.stream''submission.accepted''submission.rejected''submission.repair''request.opened''request.answered''log.appended''agent.stats''engine.recovered''engine.stopping''workflow.registered''workflow.replaced''workflow.unregistered'
GraphBranch¶
The tasks of one node that belong to one branch of a fan-out.
from_task is the fan-out that produced the branch — the
lineage.from of the first task in the branch chain whose parent
returned a list — and is null for a node reached by a single path,
which has exactly one branch.
| Field | Type | Required | Description |
|---|---|---|---|
from_task |
integer | null |
no | The fan-out task this branch came out of. |
tasks |
array of integer |
no | This node's attempts in that branch, oldest first. |
GraphEdge¶
One arrow of the finalized graph, and how often this run took it.
from is a Python keyword, so the field is from_ and carries
from as its alias — the name 08 fixes on the wire.
| Field | Type | Required | Description |
|---|---|---|---|
from |
string |
yes | The node the arrow leaves. |
kind |
EdgeKind |
yes | Forward, a loop back, or an arrow into a join. |
to |
string |
yes | The node it points at. |
traversed |
integer |
yes | How often this run took the edge: task.enqueued reason=transition between the two nodes, plus join.arrived for a join edge. |
GraphNode¶
One node of the graph, with this run's history on it.
| Field | Type | Required | Description |
|---|---|---|---|
arrivals |
Arrivals | null |
no | For a join with a fan-out still open, how much of it has arrived; absent otherwise. |
attempts |
integer |
yes | How many task rows this node has in this run. |
branches |
array of GraphBranch |
no | The node's tasks grouped by the fan-out that produced them. |
generation |
integer |
yes | BFS depth from the start node. |
join |
boolean |
yes | Whether the node waits for a fan-out to complete. |
last_task_id |
integer | null |
no | The highest task id of the node, the one the task drawer opens; null when the node has no task. |
live |
boolean |
yes | Whether a node-slot plugin panel should show: the node is in progress or waiting, or it has at least one done attempt. |
name |
string |
yes | The node's name. |
state |
NodeState |
yes | What the node is doing, by 08's precedence. |
GraphOut¶
The graph of one run (/graph).
| Field | Type | Required | Description |
|---|---|---|---|
edges |
array of GraphEdge |
yes | Every edge of the finalized graph. |
nodes |
array of GraphNode |
yes | Every node of the workflow. |
Health¶
Liveness, version and counts. Unauthenticated, and carries no ids.
| Field | Type | Required | Description |
|---|---|---|---|
ok |
boolean |
yes | True whenever the server answers at all. |
pools |
object of PoolHealth | null |
no | Every registered pool by name. Omitted when this application has no engine; empty when the engine has no pools. |
runs_running |
integer | null |
no | Runs in the running status. Omitted when this application has no store to ask. |
tasks_in_progress |
integer | null |
no | Attempts in the in_progress status; a task parked on a human is waiting and is not counted. Omitted when this application has no store to ask. |
version |
string |
yes | The running Athanore version. |
JoinArrived¶
One per branch reaching a join.
arrived == count means the join task was enqueued in the same
transaction.
| Field | Type | Required | Description |
|---|---|---|---|
arrived |
integer |
yes | |
count |
integer |
yes | |
fanout_task |
integer |
yes | |
index |
integer |
yes | |
join |
string |
yes | |
late |
boolean |
yes |
JoinArrivedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
JoinArrived |
yes | |
id |
integer | null |
no | |
name |
'join.arrived' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
LogAppended¶
preview is the first 200 characters of the entry.
| Field | Type | Required | Description |
|---|---|---|---|
author |
string |
yes | |
kind |
string | null |
no | |
log_id |
integer |
yes | |
node |
string |
yes | |
preview |
string |
yes |
LogAppendedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
LogAppended |
yes | |
id |
integer | null |
no | |
name |
'log.appended' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
LogAuthor¶
Who wrote a work-log entry.
'agent''engine''user'
LogEntry¶
One entry of a run's work log, append-only.
The work log is the inter-stage channel: the engine's failures,
the operator's notes and every agent deliverable, in one ordered list.
kind is absent on a plain entry.
| Field | Type | Required | Description |
|---|---|---|---|
author |
LogAuthor |
yes | Who wrote it. |
created |
string (date-time) |
yes | When it was written. |
id |
integer |
yes | The entry id; the log is ordered by it. |
kind |
LogKind | null |
no | What it is, for filtering; absent on a plain entry. |
node |
string |
yes | The node it was written under; user for an operator note with no node in flight. |
run_id |
string |
yes | The run the entry belongs to. |
task_id |
integer | null |
no | The attempt that wrote it, when one did. |
text |
string |
yes | The entry itself, never truncated. |
LogKind¶
What a work-log entry is, for filtering (the agent view drops
stats).
'deliverable''note''stats''failure'
LogRef¶
The id of a work-log entry that was appended.
| Field | Type | Required | Description |
|---|---|---|---|
log_id |
integer |
yes | The id of the entry that was written. |
LogText¶
Append an entry to a run's work log (POST /api/runs/{id}/log).
| Field | Type | Required | Description |
|---|---|---|---|
text |
string |
yes | The entry, written verbatim and never truncated. |
Me¶
What the caller may do, and which server they are talking to.
| Field | Type | Required | Description |
|---|---|---|---|
auth |
'off', 'token' |
yes | token when operator endpoints require a bearer token, off on a plain loopback bind. |
authenticated |
boolean |
yes | Whether this request carries operator rights. Always true when auth is off. |
features |
array of string |
yes | Optional capabilities this server has. Empty in v1. |
started_at |
string (date-time) |
yes | When this process came up. A change means a restart, which is the SPA's cue to refetch the plugin manifest. |
version |
string |
yes | The running Athanore version. |
Move¶
Move a task's work to another node (POST /api/tasks/{id}/move).
| Field | Type | Required | Description |
|---|---|---|---|
node |
string |
yes | The node to enqueue the work at. |
NewRun¶
Submit a run of a workflow (POST /api/workflows/{name}/runs).
The two fields become the start task's payload, which is why editing either afterwards does not rewrite it.
| Field | Type | Required | Description |
|---|---|---|---|
description |
string |
no | Longer context for the run; the workflow's input. |
title |
string |
yes | The operator's title for the run. |
NodeOut¶
One node's edges and its options, as 08 §Workflows lists them.
priority and retries of null mean "the server default"
and timeout of null means no wall-clock cap on one attempt —
the node metadata of 04 §Node options, unresolved, because what the
default is belongs to the settings of the server running it.
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | null |
no | The body's docstring, unless the node overrode it. |
edges |
array of string |
yes | The nodes this one may transition to, in declaration order. |
generation |
integer |
yes | BFS depth from the start node; a loop-back edge targets a generation at or below its source. |
label |
string |
yes | The display name; the function's name by default. |
priority |
integer | null |
no | Dispatch priority, or null for the server default. |
retries |
integer | null |
no | Retry budget, or null for the server default. |
timeout |
number | null |
no | Seconds one attempt may run, or null for no cap. |
NodeState¶
What a node is doing in one run, in 08 §Graph semantics' precedence.
Declared in precedence order, highest first, so the rule is the
member order and not a table kept somewhere else. failed outranks
done but is itself outranked by ready: a failed attempt whose
retry is queued reports ready, because that retry row exists and
the node is going to run again. idle means no task of this node
was ever created in this run.
The SPA colours by this alone.
'in_progress''waiting''ready''dead_letter''failed''done''cancelled''idle'
Ok¶
An operation that succeeded and has nothing else to report.
note is the one place a router adds a sentence about how it
succeeded — 08 §Runs' {ok, note?} — and is omitted when there is
nothing to say rather than sent as an empty string.
| Field | Type | Required | Description |
|---|---|---|---|
note |
string | null |
no | What was unusual about the success, when anything was. |
ok |
boolean |
no | Always true; a failure is an error body. |
PanelKind¶
The renderer vocabulary of 09 §Panel kinds, one member per row.
The kind fixes the shape source must return: markdown a
string, kv an object, table {columns, rows}, log a
list of entries, chart {series, kind}, dashboard
{note?, metrics, table?}, form the name of an action, and
custom nothing at all — the plugin's own web component reads what
it needs through window.athanore.
A kind the SPA does not know renders a placeholder card rather than crashing the pane, so this enum is the server's vocabulary and not a promise about the browser's.
'markdown''kv''table''log''chart''dashboard''form''custom'
PanelOut¶
One panel of the manifest.
source is the mounted URL of the route the panel's data comes
from, or — for a form panel — the name of the action whose model
is the form. node is present only on a panel that follows one;
whether that node is live travels on the run's graph rather than
here, because the manifest changes only when a workflow is
registered, replaced or removed, never per run (08 §Graph semantics,
22 §Live mounting).
| Field | Type | Required | Description |
|---|---|---|---|
element |
string | null |
no | The custom element tag a custom panel renders. |
kind |
PanelKind |
yes | Which renderer draws it, and its data shape. |
name |
string |
yes | The panel's title, unique within its workflow. |
node |
string | null |
no | The node this panel follows, when it follows one. |
placement |
'pane', 'card' |
yes | A pane of its own, or a card on the run overview. |
refresh_on |
array of string |
no | Event-name globs that invalidate this panel's data. |
scope |
Slot |
yes | What has to be resolved before it can load. |
slot |
Slot |
yes | Where the panel is shown. |
source |
string | null |
no | The URL its data comes from, or the action name for a form. |
PluginEvent¶
plugin.<workflow>.<name>: the vocabulary's open end.
The handler's data is free-form, so the wire carries whatever it passed
to ctx.services.events.publish; 18 requires a JSON object, and the
registry — not this model — enforces that <workflow> is the
publishing workflow.
name is the only one in the union that is not a
Literal, and it is declared as the whole vocabulary
— EventName | str — rather than as a bare str. The two are
the same set of strings, so nothing is widened; what the wider
spelling buys is a reference to the enum in the generated document,
which is where 08 §OpenAPI wants the event-name enum published and
where the SPA's TypeScript union is generated from. The validator below is what actually narrows this field, to
the plugin. namespace.
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
object |
yes | |
id |
integer | null |
no | |
name |
EventName | string |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
PluginManifestEntry¶
What one workflow contributes to the UI.
| Field | Type | Required | Description |
|---|---|---|---|
actions |
array of ActionOut |
no | Its actions, in declaration order. |
assets |
array of string |
no | URLs of the JavaScript modules the SPA injects for it, each carrying a ?v= that changes when the file's bytes do. |
panels |
array of PanelOut |
no | Its panels, in declaration order. |
workflow |
string |
yes | The workflow that declared these, or _builtin for the views the core ships. |
PoolHealth¶
One pool's capacity and what it is spending right now.
| Field | Type | Required | Description |
|---|---|---|---|
capacity |
integer |
yes | Slots the pool has, in total. |
in_flight |
integer |
yes | Slots leased right now. |
Position¶
Move a run in the dispatch list (POST /api/runs/{id}/position).
Exactly one of the two. direction swaps with the neighbour above
(-1) or below (+1) and is a no-op at the ends — still a 200,
reporting the position the run already had. index is the
zero-based list index to move to, clamped to the list; New Run's
"top" is {"index": 0} and "bottom" is the default, which needs no
call at all.
| Field | Type | Required | Description |
|---|---|---|---|
direction |
-1, 1 | null |
no | Swap with the neighbour above (-1) or below (1). |
index |
integer | null |
no | The zero-based list index to move to, clamped. |
PositionOut¶
Where a run sits in the dispatch list after a move.
| Field | Type | Required | Description |
|---|---|---|---|
position |
integer |
yes | The run's place in the dispatch list after the move, numbered from 1. The index that asked for it is zero-based. |
RegisterWorkflow¶
Register a workflow on the running server (POST /api/workflows).
target names the workflow as athanore serve would: module:attr
or path/to/file.py:attr. The name it registers under is the loaded
Workflow's own. pool must already exist on the engine — a
live registration never creates one — and persist writes the
[workflows.<name>] row of athanore.toml before anything is
mutated, so a target that does not load is never written down.
| Field | Type | Required | Description |
|---|---|---|---|
persist |
boolean |
no | Write the registration as a [workflows.<name>] row of athanore.toml; the response then carries X-Athanore-Persisted. |
pool |
string | null |
no | The pool to bind the workflow to; the default pool when omitted. |
target |
string |
yes | The workflow to load: module:attr or path/to/file.py:attr. |
ReloadWorkflow¶
Replace a registered workflow (PUT /api/workflows/{name}).
target omitted re-resolves the registration's recorded target —
what athanore serve or an earlier registration loaded it from — and
is refused when there is none. A target that now defines a differently
named workflow is refused too: that is a new workflow, and POST is
how it arrives. pool omitted keeps the binding the name has.
| Field | Type | Required | Description |
|---|---|---|---|
persist |
boolean |
no | Rewrite the [workflows.<name>] row of athanore.toml; the response then carries X-Athanore-Persisted. |
pool |
string | null |
no | The pool to move the workflow to; the current binding when omitted. Refused while any attempt of the workflow is in flight. |
target |
string | null |
no | The workflow to load; the registration's recorded target when omitted. |
RemovedWorkflowOut¶
What DELETE /api/workflows/{name} did.
A body rather than a 204, because the body is the point: the caller
wants to know what it interrupted. Whether a row of
athanore.toml was removed is the response's X-Athanore-Persisted
header, not a field — a receipt about the request belongs on the
response.
| Field | Type | Required | Description |
|---|---|---|---|
task_ids |
array of integer |
yes | The attempts that were interrupted, in spawn order; empty when nothing of the workflow was in flight. |
workflow |
string |
yes | The name that was unregistered. |
RequestAnswered¶
value is not in the event (may be large or sensitive); fetch it.
| Field | Type | Required | Description |
|---|---|---|---|
author |
'user', 'engine' |
yes | |
option_id |
string | null |
no | |
request_id |
integer |
yes |
RequestAnsweredEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RequestAnswered |
yes | |
id |
integer | null |
no | |
name |
'request.answered' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RequestKind¶
What the request is about. A label for the UI; nothing keys on it.
'permission''elicitation''question'
RequestMode¶
The shape of the answer a request expects.
'options''form''text'
RequestOpened¶
| Field | Type | Required | Description |
|---|---|---|---|
kind |
string |
yes | |
mode |
string |
yes | |
node |
string |
yes | |
ordinal |
integer | null |
no | |
request_id |
integer |
yes | |
source |
string |
yes |
RequestOpenedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RequestOpened |
yes | |
id |
integer | null |
no | |
name |
'request.opened' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RequestOption¶
One choice an options request offers.
The agent's options verbatim for an ACP permission: the operator
picks under the labels the agent used, and the option_id that
goes back is the one it will be given.
| Field | Type | Required | Description |
|---|---|---|---|
kind |
string | null |
no | The ACP permission kind, when the option came from one. |
name |
string |
yes | What to show the operator. |
option_id |
string |
yes | The id an answer names. |
RequestSource¶
Who raised the request: the agent mid-turn, or the node body.
'agent''node'
RequestView¶
A request, its answer, and the node that asked.
schema is spelled schema_ in Python — a field named schema
shadows an attribute of BaseModel — and keeps schema as its
alias, which is the name 08 fixes on the wire.
| Field | Type | Required | Description |
|---|---|---|---|
age |
number |
yes | Seconds since it was opened, when it was read. |
answer |
any |
no | The chosen option_id, or the value of a text or form answer. Null both before an answer and for an answer that was null. |
answered_by |
AnswerAuthor | null |
no | Who answered; the field to test for having an answer at all. |
created |
string (date-time) |
yes | When the request was opened. |
id |
integer |
yes | The request id. |
kind |
RequestKind |
yes | What the request is about; a label. |
mode |
RequestMode |
yes | The shape of the answer expected. |
node |
string |
yes | The node that attempt runs. |
options |
array of RequestOption | null |
no | The choices, for an options request. |
pending |
boolean |
yes | Unanswered, with an attempt still waiting. |
prompt |
string |
yes | What the operator is being asked. |
run_id |
string |
yes | The run it belongs to. |
schema |
object | null |
no | The JSON Schema the answer must fit, for a form request. |
source |
RequestSource |
yes | Whether the agent asked mid-turn or the node body did. |
stale |
boolean |
yes | Unanswered, with no attempt left to consume an answer: in history, out of the inbox, and no longer answerable. |
task_id |
integer |
yes | The attempt that asked. |
tool_call |
object | null |
no | What the agent was about to do, for a permission request. |
Rerun¶
Run a node again (POST /api/runs/{id}/rerun).
The payload and branch it last had are re-used; a rerun of a join replays the arrivals it fired on.
| Field | Type | Required | Description |
|---|---|---|---|
node |
string |
yes | The node to run again. |
RunCancelled¶
| Field | Type | Required | Description |
|---|---|---|---|
cancelled_tasks |
array of integer |
yes |
RunCancelledEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunCancelled |
yes | |
id |
integer | null |
no | |
name |
'run.cancelled' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunChanges¶
The fields an edit or an op changed on a run; only those it changed.
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | null |
no | |
status |
string | null |
no | |
title |
string | null |
no |
RunCompleted¶
output follows the shape rule of 04 §Routing edge cases.
node and task_id are the last landing task. output is
required and may be null: a body that returns None terminates
the run with that value, which is not the same as an unknown one.
| Field | Type | Required | Description |
|---|---|---|---|
node |
string |
yes | |
output |
any |
yes | |
task_id |
integer |
yes | |
terminal_tasks |
array of integer |
yes |
RunCompletedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunCompleted |
yes | |
id |
integer | null |
no | |
name |
'run.completed' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunCreated¶
| Field | Type | Required | Description |
|---|---|---|---|
position |
integer |
yes | |
title |
string |
yes | |
workflow |
string |
yes |
RunCreatedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunCreated |
yes | |
id |
integer | null |
no | |
name |
'run.created' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunDeleted¶
The last event of a run: it is deleted with the run, so SSE only.
| Field | Type | Required | Description |
|---|---|---|---|
title |
string |
yes | |
workflow |
string |
yes |
RunDeletedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunDeleted |
yes | |
id |
integer | null |
no | |
name |
'run.deleted' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunDetail¶
A run, its tasks and its totals.
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | When the run was submitted. |
current_nodes |
array of string |
no | The nodes with an in-progress or waiting attempt right now. |
description |
string |
no | The operator's description. |
id |
string |
yes | The run id, a ULID. |
output |
any |
no | The run's output: one value for a single terminal task, a list in branch order when several branches terminated. Null before the run ends, and also when it ended by returning null. |
outputs |
array of RunOutput |
no | One entry per terminal task, always a list. |
pending_requests |
integer |
no | Unanswered, non-stale requests waiting on a person. |
position |
integer |
yes | The run's place in the dispatch list; runs are numbered 1..n in list order. |
stats |
RunStats |
no | The agent totals of the run. |
status |
RunStatus |
yes | The run state machine of 03. |
tasks |
array of TaskView |
no | Every attempt of the run, oldest first. |
title |
string |
yes | The operator's title for the run. |
unregistered |
boolean |
no | Whether this server has no workflow of that name registered. |
updated |
string (date-time) |
yes | When anything about it last changed. |
workflow |
string |
yes | The workflow this run executes. |
RunFailed¶
code is set when the run stalled on a partial join.
| Field | Type | Required | Description |
|---|---|---|---|
code |
'join_incomplete' | null |
no | |
error |
string |
yes | |
node |
string |
yes | |
task_id |
integer |
yes |
RunFailedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunFailed |
yes | |
id |
integer | null |
no | |
name |
'run.failed' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunOutput¶
One terminal task's value, with the branch it came out of.
| Field | Type | Required | Description |
|---|---|---|---|
branch |
array of BranchRef |
no | The fan-out stack the attempt was inside, outermost first; empty for a run that never fanned out. |
node |
string |
yes | The node that attempt ran. |
task_id |
integer |
yes | The terminal attempt that produced it. |
value |
any |
no | What the body returned. |
RunPaused¶
Type: object
RunPausedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunPaused |
yes | |
id |
integer | null |
no | |
name |
'run.paused' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunReordered¶
| Field | Type | Required | Description |
|---|---|---|---|
position |
integer |
yes | |
previous |
integer |
yes |
RunReorderedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunReordered |
yes | |
id |
integer | null |
no | |
name |
'run.reordered' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunResumed¶
Type: object
RunResumedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunResumed |
yes | |
id |
integer | null |
no | |
name |
'run.resumed' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunStarted¶
The first claim of the run.
| Field | Type | Required | Description |
|---|---|---|---|
node |
string |
yes | |
task_id |
integer |
yes |
RunStartedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunStarted |
yes | |
id |
integer | null |
no | |
name |
'run.started' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
RunStats¶
A run's agent totals, summed over its attempts.
Every field is optional and nothing is zero-filled: a provider that
reported no cost leaves cost absent rather than claiming zero (01
§Real data only). duration_s is the sum of the attempts' durations
and not wall-clock time, which fan-out would make meaningless.
| Field | Type | Required | Description |
|---|---|---|---|
cost |
number | null |
no | |
duration_s |
number | null |
no | |
input_tokens |
integer | null |
no | |
output_tokens |
integer | null |
no | |
tool_calls |
integer | null |
no | |
total_tokens |
integer | null |
no |
RunStatus¶
The run state machine of 03.
queued is new in v1: a run that exists but has dispatched nothing,
so "waiting for a slot" is visible. The three terminal states are
re-openable by retry, rerun and move.
'queued''running''paused''completed''failed''cancelled'
RunSummary¶
One row of the run list.
current_nodes is plural because a fan-out puts a run in several
nodes at once. unregistered is not stored: it says this server has
no workflow of that name registered, so the run is shown but nothing
will dispatch it.
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | When the run was submitted. |
current_nodes |
array of string |
no | The nodes with an in-progress or waiting attempt right now. |
id |
string |
yes | The run id, a ULID. |
pending_requests |
integer |
no | Unanswered, non-stale requests waiting on a person. |
position |
integer |
yes | The run's place in the dispatch list; runs are numbered 1..n in list order. |
status |
RunStatus |
yes | The run state machine of 03. |
title |
string |
yes | The operator's title for the run. |
unregistered |
boolean |
no | Whether this server has no workflow of that name registered. |
updated |
string (date-time) |
yes | When anything about it last changed. |
workflow |
string |
yes | The workflow this run executes. |
RunUpdated¶
| Field | Type | Required | Description |
|---|---|---|---|
changed |
RunChanges |
yes |
RunUpdatedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
RunUpdated |
yes | |
id |
integer | null |
no | |
name |
'run.updated' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
SetStatus¶
Write one of the three statuses an operator may set.
The other four are the engine's own record of what happened and are not an operator's to declare.
| Field | Type | Required | Description |
|---|---|---|---|
status |
'ready', 'cancelled', 'dead_letter' |
yes | ready re-dispatches the task, cancelled stops it, dead_letter files it as failed for good. |
Slot¶
Where a panel is shown — 09 §Slots, one member per row.
run is a pane in the selected run's cycle or a card on its
overview, task the task drawer, node a pane live only while
the named node has a task in flight or has produced output,
workflow the library's detail side, and global a pane shown
when no run is selected.
'run''task''node''workflow''global'
SourceNode¶
Where one node's body starts in the returned source.
| Field | Type | Required | Description |
|---|---|---|---|
line |
integer |
yes | The 1-based line of the body's first decorator or def. |
SourceOut¶
A workflow's module source, and the line each node begins on.
nodes names the nodes whose bodies are defined in this file.
A workflow may register a function imported from somewhere else; that
node has no line in this source, and it is left out rather than given
a number that points at the wrong text.
| Field | Type | Required | Description |
|---|---|---|---|
file |
string |
yes | The absolute path of the module the source is from. |
nodes |
object of SourceNode |
yes | The line each node's body starts on, for the nodes defined here. |
source |
string |
yes | That module's text, in full. |
StreamChunk¶
One segment of an agent transcript.
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | When it was recorded. |
kind |
ChunkKind |
yes | What kind of segment this is. |
seq |
integer |
yes | The per-task cursor ?after= pages by. |
text |
string |
yes | The segment itself. |
StreamOut¶
A page of one task's transcript.
last_seq is the highest sequence stored for the task, not the
highest in this page, so a client that has caught up can tell. live
says the attempt is still running — in_progress or waiting,
because a waiting attempt goes on writing once its request is
answered — which is what stops the SPA polling a transcript that will
never grow again.
| Field | Type | Required | Description |
|---|---|---|---|
chunks |
array of StreamChunk |
yes | The page, in sequence order. |
last_seq |
integer |
yes | The highest sequence stored for this task. |
live |
boolean |
yes | Whether the attempt is still running (in_progress or waiting), so the transcript may still grow. |
SubmissionAccepted¶
The payload itself is not in the event; fetch the task.
| Field | Type | Required | Description |
|---|---|---|---|
node |
string |
yes | |
submission_id |
integer |
yes |
SubmissionAcceptedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
SubmissionAccepted |
yes | |
id |
integer | null |
no | |
name |
'submission.accepted' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
SubmissionOut¶
One value an agent submitted for a task.
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | When it was accepted. |
id |
integer |
yes | The submission id. |
payload |
any |
no | The value, as it was accepted. |
task_id |
integer |
yes | The attempt it was submitted for. |
SubmissionRejected¶
| Field | Type | Required | Description |
|---|---|---|---|
errors |
array of ValidationError |
yes | |
node |
string |
yes |
SubmissionRejectedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
SubmissionRejected |
yes | |
id |
integer | null |
no | |
name |
'submission.rejected' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
SubmissionRepair¶
| Field | Type | Required | Description |
|---|---|---|---|
node |
string |
yes | |
reason |
'nothing_submitted', 'rejected' |
yes | |
turn |
integer |
yes |
SubmissionRepairEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
SubmissionRepair |
yes | |
id |
integer | null |
no | |
name |
'submission.repair' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskCancelled¶
| Field | Type | Required | Description |
|---|---|---|---|
from |
string |
yes | |
node |
string |
yes | |
reason |
'cancel', 'move', 'delete', 'set_status' |
yes |
TaskCancelledEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskCancelled |
yes | |
id |
integer | null |
no | |
name |
'task.cancelled' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskDeadLettered¶
| Field | Type | Required | Description |
|---|---|---|---|
attempt |
integer |
yes | |
error |
string |
yes | |
node |
string |
yes |
TaskDeadLetteredEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskDeadLettered |
yes | |
id |
integer | null |
no | |
name |
'task.dead_lettered' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskDetail¶
A task and the values submitted against it.
The operator view: 08 overloads nothing by credential, so an agent
reading its own task gets a different model on a different router
(/api/agent/), and this one never has a token to omit.
| Field | Type | Required | Description |
|---|---|---|---|
attempt |
integer |
yes | Which attempt of that node this row is, from 1. |
branch |
array of athanore__api__schemas__tasks__BranchFrame |
no | The fan-out stack this attempt is inside. |
created |
string (date-time) |
yes | When the row was enqueued. |
error |
string | null |
no | Why the attempt failed, when it did. |
explicit |
boolean |
yes | Whether that priority was declared on the node rather than defaulted. |
finished |
string (date-time) | null |
no | When it ended. |
id |
integer |
yes | The task id, unique across every run. |
lineage |
object | null |
no | Where this attempt came from: its reason and its parent. |
node |
string |
yes | The node whose body this attempt runs. |
payload |
any |
no | The value the task was enqueued with. |
priority |
integer |
yes | The dispatch priority this attempt was given. |
result |
any |
no | What the body returned, once it has. |
run_id |
string |
yes | The run this attempt belongs to. |
started |
string (date-time) | null |
no | When it was claimed. |
stats |
object | null |
no | The agent's measurements for this attempt; absent when nothing measured it. |
status |
TaskStatus |
yes | The task state machine of 03. |
submissions |
array of SubmissionOut |
no | Every accepted submission, oldest first. |
terminal |
boolean |
yes | Whether the attempt finished with no transition, making its result a branch output. |
TaskDone¶
| Field | Type | Required | Description |
|---|---|---|---|
attempt |
integer |
yes | |
node |
string |
yes | |
terminal |
boolean |
yes | |
transitions |
array of string |
yes |
TaskDoneEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskDone |
yes | |
id |
integer | null |
no | |
name |
'task.done' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskEnqueued¶
branch omits key; for reason=join from_task is the
fan-out task and arrivals lists the arriving tasks.
| Field | Type | Required | Description |
|---|---|---|---|
arrivals |
array of integer | null |
no | |
attempt |
integer |
yes | |
branch |
array of athanore__events__payloads__BranchFrame |
yes | |
from_task |
integer | null |
no | |
node |
string |
yes | |
payload_present |
boolean |
yes | |
reason |
'start', 'transition', 'retry', 'rerun', 'move', 'manual_retry', 'set_status', 'join' |
yes |
TaskEnqueuedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskEnqueued |
yes | |
id |
integer | null |
no | |
name |
'task.enqueued' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskFailed¶
| Field | Type | Required | Description |
|---|---|---|---|
attempt |
integer |
yes | |
error |
string |
yes | |
node |
string |
yes | |
retry_task_id |
integer | null |
no | |
retryable |
boolean |
yes | |
will_retry |
boolean |
yes |
TaskFailedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskFailed |
yes | |
id |
integer | null |
no | |
name |
'task.failed' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskMoved¶
| Field | Type | Required | Description |
|---|---|---|---|
new_task_id |
integer |
yes | |
node |
string |
yes | |
to |
string |
yes |
TaskMovedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskMoved |
yes | |
id |
integer | null |
no | |
name |
'task.moved' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskRef¶
The id of a task an operator verb queued.
The answer to rerun, retry and move: each of the three
ends with a new attempt row, and this is its id (08 §Runs,
§Tasks).
| Field | Type | Required | Description |
|---|---|---|---|
task_id |
integer |
yes | The id of the task that was enqueued. |
TaskResumed¶
| Field | Type | Required | Description |
|---|---|---|---|
node |
string |
yes | |
request_id |
integer |
yes | |
waited_s |
number |
yes |
TaskResumedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskResumed |
yes | |
id |
integer | null |
no | |
name |
'task.resumed' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskStarted¶
| Field | Type | Required | Description |
|---|---|---|---|
attempt |
integer |
yes | |
node |
string |
yes |
TaskStartedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskStarted |
yes | |
id |
integer | null |
no | |
name |
'task.started' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskStatus¶
The task state machine of 03.
waiting is a body parked in human_input; it holds no pool slot. failed is the record of one attempt — the retry is a
new row — and dead_letter is the attempt that exhausted them.
'ready''in_progress''waiting''done''failed''dead_letter''cancelled'
TaskStatusSet¶
| Field | Type | Required | Description |
|---|---|---|---|
from |
string |
yes | |
node |
string |
yes | |
to |
string |
yes |
TaskStatusSetEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskStatusSet |
yes | |
id |
integer | null |
no | |
name |
'task.status_set' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskStream¶
Ephemeral; the content is fetched from /api/tasks/{id}/stream.
| Field | Type | Required | Description |
|---|---|---|---|
seq_from |
integer |
yes | |
seq_to |
integer |
yes |
TaskStreamEvent¶
Ephemeral: published to the bus and SSE, never stored, no id.
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskStream |
yes | |
id |
integer | null |
no | |
name |
'task.stream' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
TaskView¶
One attempt of one node in one run. Never carries a token.
| Field | Type | Required | Description |
|---|---|---|---|
attempt |
integer |
yes | Which attempt of that node this row is, from 1. |
branch |
array of athanore__api__schemas__tasks__BranchFrame |
no | The fan-out stack this attempt is inside. |
created |
string (date-time) |
yes | When the row was enqueued. |
error |
string | null |
no | Why the attempt failed, when it did. |
explicit |
boolean |
yes | Whether that priority was declared on the node rather than defaulted. |
finished |
string (date-time) | null |
no | When it ended. |
id |
integer |
yes | The task id, unique across every run. |
lineage |
object | null |
no | Where this attempt came from: its reason and its parent. |
node |
string |
yes | The node whose body this attempt runs. |
payload |
any |
no | The value the task was enqueued with. |
priority |
integer |
yes | The dispatch priority this attempt was given. |
result |
any |
no | What the body returned, once it has. |
run_id |
string |
yes | The run this attempt belongs to. |
started |
string (date-time) | null |
no | When it was claimed. |
stats |
object | null |
no | The agent's measurements for this attempt; absent when nothing measured it. |
status |
TaskStatus |
yes | The task state machine of 03. |
terminal |
boolean |
yes | Whether the attempt finished with no transition, making its result a branch output. |
TaskWaiting¶
| Field | Type | Required | Description |
|---|---|---|---|
node |
string |
yes | |
request_id |
integer |
yes |
TaskWaitingEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
TaskWaiting |
yes | |
id |
integer | null |
no | |
name |
'task.waiting' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
ValidationError¶
One pydantic error, as ValidationError.errors() reports it.
| Field | Type | Required | Description |
|---|---|---|---|
loc |
array of string | integer |
yes | |
msg |
string |
yes | |
type |
string |
yes |
WorkflowOut¶
One registered workflow: its graph, its capacity, its plugins.
| Field | Type | Required | Description |
|---|---|---|---|
capacity |
integer |
yes | Slots that pool has, in total. |
in_flight |
integer |
yes | Slots of that pool leased right now, across every workflow bound to it. |
name |
string |
yes | The workflow's name, as it appears in URLs. |
nodes |
object of NodeOut |
yes | Every node of the finalized graph, by name. |
plugin |
WorkflowPlugin |
yes | What this workflow contributes to the UI. |
pool |
string |
yes | The pool this workflow's tasks are dispatched on. |
start |
string |
yes | The node a new run begins at. |
target |
string | null |
no | The target this workflow was loaded from (module:attr or path.py:attr), and what PUT /api/workflows/{name} without a target reloads; null for a programmatic registration. |
WorkflowPlugin¶
The panels and actions a workflow contributes.
Both lists are empty until the plugin registry lands: the field is part of the contract now so the SPA can read a workflow's plugin surface from the workflow itself, and the entries it will carry are the manifest's own.
| Field | Type | Required | Description |
|---|---|---|---|
actions |
array of object |
no | Action declarations, as /api/plugins lists them. |
panels |
array of object |
no | Panel declarations, as /api/plugins lists them. |
WorkflowRegistered¶
A workflow added to a serving server. No run_id.
target is the string it was loaded from, absent for a
programmatic registration.
| Field | Type | Required | Description |
|---|---|---|---|
pool |
string |
yes | |
target |
string | null |
no | |
workflow |
string |
yes |
WorkflowRegisteredEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
WorkflowRegistered |
yes | |
id |
integer | null |
no | |
name |
'workflow.registered' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
WorkflowReplaced¶
A workflow's graph and plugins swapped. No run_id.
| Field | Type | Required | Description |
|---|---|---|---|
pool |
string |
yes | |
target |
string | null |
no | |
workflow |
string |
yes |
WorkflowReplacedEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
WorkflowReplaced |
yes | |
id |
integer | null |
no | |
name |
'workflow.replaced' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
WorkflowUnregistered¶
A workflow removed. No run_id.
task_ids are the attempts the removal interrupted.
| Field | Type | Required | Description |
|---|---|---|---|
task_ids |
array of integer |
yes | |
workflow |
string |
yes |
WorkflowUnregisteredEvent¶
| Field | Type | Required | Description |
|---|---|---|---|
created |
string (date-time) |
yes | |
data |
WorkflowUnregistered |
yes | |
id |
integer | null |
no | |
name |
'workflow.unregistered' |
yes | |
run_id |
string | null |
no | |
task_id |
integer | null |
no |
athanore__api__schemas__tasks__BranchFrame¶
One frame of a task's fan-out stack.
Frames nest, outermost first: a branch that fans out again pushes a
second frame. key is the payload that branch was given, which is
the branch's identity in every list the SPA groups by it.
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer |
yes | How many branches that fan-out opened. |
fanout |
integer |
yes | The task whose return value fanned out. |
index |
integer |
yes | This branch's zero-based index in that fan-out. |
key |
any |
no | The payload this branch was given. |
athanore__events__payloads__BranchFrame¶
One fan-out frame of a task's branch, without its (large) key.
| Field | Type | Required | Description |
|---|---|---|---|
count |
integer |
yes | |
fanout |
integer |
yes | |
index |
integer |
yes |
The document all of this is generated from is published beside this page: openapi.json.