Manage Hoody agent workflow definitions and drive workflow run lifecycle. Use the definition routes to list, read, create/replace, hide, and delete multi-step workflow files visible to a cwd/config_dir. Use the run routes to dispatch a workflow onto a live session, snapshot and inspect in-flight or recent runs, cancel an in-flight run, or resume a failed/cancelled run. Realm scoping is enforced by the gateway itself: the realm is the gateway’s pinned realm (or its active realm when unpinned), and a client-supplied X-Hoody-Realm / ?realm= selector cannot pivot the request into another realm.
All endpoints run on the agent service inside the project container.
List the workflow definitions visible to the requesting cwd/config_dir. Realm-scoped to the gateway’s own realm; a client-supplied X-Hoody-Realm or ?realm= is ignored and cannot select another realm’s definitions.
Read one workflow definition. Returns the definition wrapped in the tool {output,is_error} envelope. With ?include_revision=true, the output’s first line is revision: r1:<64hex> — the optimistic-concurrency baseline to send back as putWorkflow’s expected_revision — and the JSON below it is unchanged.
If "true", the tool output’s first line is revision: <opaque> — pass that value as putWorkflow’s expected_revision to guard against concurrent edits; the JSON below it is unchanged. Strictly parsed: exactly one value, "true" or "false"; anything else (empty, "TRUE", "1", repeated) is a 400 bad_request.
Create or replace one workflow definition. The body carries the definition object; the {name} path value is authoritative. The daemon validates the definition strictly before writing atomically (unknown fields rejected, the loader’s structural gate enforced), so a malformed definition is refused (is_error:true) rather than corrupting workflow.json. The system flag is authoritative from the embedded defaults — a caller cannot forge it.
The full workflow definition object (steps, entry_point, summary). Validated strictly server-side before an atomic write.
expected_revision
string
No
Optional optimistic-concurrency guard: the revision: value from getWorkflow (?include_revision=true). If the stored workflow changed since that read, the upsert is refused with [revision_conflict] and nothing is written. Omit to save unconditionally.
expected_absent
boolean
No
Optional create-only guard: refuse with [already_exists] (writing nothing) if any workflow with this name already exists. Use when creating a new workflow that must not overwrite an existing one. Mutually exclusive with expected_revision.
The request was malformed or carried invalid parameters.
Correct the request body or query parameters.
tool_mutation_refused
Tool mutation refused
A sessionless tool run resolved to a mutating tool with no confirmation posture. Sessionless runs are read-only by default.
Open a session and run the tool there, or supply allow_mutations:true / confirm:true on the sessionless run.
realm_scope_unsupported
Realm scope unsupported
A per-request realm header was supplied to an active-only / global-no-realm RPC.
Omit the realm header on this route, or open a session to scope by realm.
reserved_name
Reserved workflow name
The workflow name collides with a literal /workflows/ route segment (e.g. "runs"), so its definition GET (GET /workflows/{name}) would be permanently shadowed by that route and the definition could never be read back or used as a CAS baseline.
Choose a workflow name that is not a reserved /workflows/ route segment.
{
"code":"forbidden",
"message":"request must arrive through the Hoody proxy"
}
Error Code
Title
Description
Resolution
forbidden
Forbidden (not via the Hoody proxy)
Forbidden — the request did not reach the service through the public endpoint.
Reach the agent through hoody-proxy, not by connecting to the container directly.
{
"code":"tool_not_found",
"message":"no tool with that name"
}
Error Code
Title
Description
Resolution
tool_not_found
Tool not found
No tool with the given name exists in the catalogue or in the session’s effective tool list.
List the catalogue (GET /tools) or the session’s tools (GET /sessions/{id}/tools) and use a valid name.
{
"code":"realm_unpinned_conditional_write",
"message":"conditional workflow write requires a realm-pinned gateway"
}
Error Code
Title
Description
Resolution
realm_unpinned_conditional_write
Conditional write needs a realm-pinned gateway
An If-Match (expected_revision) workflow upsert was issued through an unpinned agent gateway, which resolves its realm per request and cannot guarantee the baseline read and this write target the same realm.
Use a realm-pinned agent gateway for optimistic-concurrency (expected_revision) writes, or save unconditionally by omitting expected_revision. expected_absent (create-only) is always allowed.
{
"code":"payload_too_large",
"message":"request body exceeds the configured size limit"
}
Error Code
Title
Description
Resolution
payload_too_large
Payload too large
The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge (http.MaxBytesReader) before the handler reads it.
Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests.
{
"code":"rate_limited",
"message":"request rate limit exceeded"
}
Error Code
Title
Description
Resolution
rate_limited
Too many requests
The per-client request rate limit was exceeded.
Honor the Retry-After header and retry; reduce the request rate.
{
"code":"internal_error",
"message":"internal server error"
}
Error Code
Title
Description
Resolution
internal_error
Internal error
An unexpected error occurred while handling the request.
Hide or un-hide a workflow from the Workflows tab. Hiding is the only way to remove a system workflow from view — system workflows are re-seeded on every boot and can never be deleted; user workflows can also be hidden. Pass hidden:false in the body to un-hide. Realm-scoped to the gateway’s own realm; a client-supplied X-Hoody-Realm / ?realm= is ignored.
Dispatch a run onto a live session, snapshot and inspect in-flight or recent runs, cancel an in-flight run, or resume a failed/cancelled run. Run events flow on the owning session’s WS/SSE attach; there is no per-run event bus.
Returns a snapshot of in-flight and recently-finished workflow runs (the registry exposes Snapshot(), not Subscribe()). Poll this to track a run’s progress; live events also flow on the owning session’s stream.
Returns a single workflow run by id, including per-step outcomes (step id, status, duration, tokens, and error). The leaner listWorkflowRuns snapshot omits per-step outcomes. Returns 404 not_found when the run id is no longer retained (evicted or never existed), or belongs to another realm/account.
Dispatch a workflow run onto a live session and return {job_id}. job.run_id is null during the brief dispatch window and is populated once the workflow loop registers the run (observe workflow_start on the session’s stream). The session must already exist.
Optional input text fed to the workflow run ($(workflow.prompt)).
inputs
object
No
Optional run-time values for the workflow’s declared input parameters (declared name → string value; resolves to $(input.<name>) in every step). A workflow with a required declared parameter cannot run without these. Values must be strings; a non-string value is a 400.
The request was malformed or carried invalid parameters.
Correct the request body or query parameters.
realm_scope_unsupported
Realm scope unsupported
A per-request realm header was supplied to an active-only / global-no-realm RPC.
Omit the realm header on this route, or open a session to scope by realm.
{
"code":"forbidden",
"message":"request must arrive through the Hoody proxy"
}
Error Code
Title
Description
Resolution
forbidden
Forbidden (not via the Hoody proxy)
Forbidden — the request did not reach the service through the public endpoint.
Reach the agent through hoody-proxy, not by connecting to the container directly.
{
"code":"not_found",
"message":"resource not found"
}
Error Code
Title
Description
Resolution
not_found
Not found
The requested resource does not exist.
Verify the path and identifier.
workflow_not_found
Workflow not found
No workflow definition with the requested name is visible to this cwd/config_dir.
List the available workflows (GET /workflows) and use an existing name.
{
"code":"turn_in_flight",
"message":"a turn is already running on this session"
}
Error Code
Title
Description
Resolution
turn_in_flight
Turn in flight
A turn is already running on this session; the single serial turn slot is occupied, so a new turn/workflow run is refused.
Wait for the running turn to finish (observe agent_done on the session stream), then retry.
gate_parked
Gate parked
A confirm/question gate is parked on this session, so a new turn/workflow run is refused until it is answered. The parked gate is surfaced under details.pending_gate.
Answer the parked gate (/confirm or /answer) and retry; read it via GET /sessions/{id} or details.pending_gate.
{
"code":"payload_too_large",
"message":"request body exceeds the configured size limit"
}
Error Code
Title
Description
Resolution
payload_too_large
Payload too large
The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge (http.MaxBytesReader) before the handler reads it.
Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests.
{
"code":"rate_limited",
"message":"request rate limit exceeded"
}
Error Code
Title
Description
Resolution
rate_limited
Too many requests
The per-client request rate limit was exceeded.
Honor the Retry-After header and retry; reduce the request rate.
{
"code":"internal_error",
"message":"internal server error"
}
Error Code
Title
Description
Resolution
internal_error
Internal error
An unexpected error occurred while handling the request.
Retry; if persistent, inspect the daemon logs.
{
"code":"service_unavailable",
"message":"service unavailable"
}
Error Code
Title
Description
Resolution
service_unavailable
Service unavailable
The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit).
Resume a terminal (failed/cancelled) run in a live session. Committed steps replay from the run’s recorded journal and execution continues live from the first incomplete step. The resuming session must match the run’s realm, owner, working directory, and container binding; refusals return an actionable message.