The Agent: Todos endpoints let you file, read, update, claim, run, snooze, and archive master todos in the agent’s per-cwd todo store. You can also approve or deny run proposals, post comments to a todo’s timeline (with or without kicking an orchestrator turn), kick autonomous worker runs, and trigger an LLM triage pass over the inbox. Every operation that targets a single todo uses the todo’s {id} in the path; proposal sub-resources add {pid}.
These routes resolve the daemon’s todos.* JSON-RPC actions. The gateway holds no service-level auth of its own — hoody-proxy is the network boundary (kit network-position trust) — and forwards the body plus request-scope headers (cwd, config_dir, container, realm) to the daemon. Several of these routes are J-class ops (run, approve_proposal, triage, purge): they spend model budget on autonomous LLM activity with no _machine_confirmed gate on the daemon RPC, and the daemon’s own systemAdminGate may still 403 admin_unauthorized when a Unix-socket admin capability is configured.
Lists master todos for the requesting cwd/config_dir, with the current CAS revision. Filters ride the request body (states, tags, query, open_only, all) — there is no query-param filter alias (the daemon applies typed filters a flat query string cannot supply). Active-realm-scoped: a per-request realm header is rejected.
Per-request working-directory scope (§6.1): the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd (e.g. POST /todos; createTodo also accepts a body cwd).
X-Hoody-Config-Dir
header
string
No
Per-request --config-dir override (§6.1) selecting which on-disk .hoody install a stateless read/write resolves (HoodyPaths).
X-Hoody-Container
header
string
No
Per-request bound remote container (§6.1; omitted = local). Rejected (400) on routes with no container dimension.
X-Hoody-Realm
header
string
No
Per-request realm selector (§6.1): "global" or a 24-hex id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of the X-Hoody-Realm header (read only when the header is absent): "global" or a 24-hex id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes.
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, which has no realm dimension to scope.
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)
The request did not arrive through hoody-proxy: its source IP is private/local (in-container loopback, the bridge, the host, or a sibling container). The gateway has no service-level auth — authorization is owned by hoody-proxy, which is the only party that can reach it (TPROXY preserves the real public client IP). Kit network-position trust.
Reach the agent through hoody-proxy (e.g. hoody agent … → platform → proxy), not by connecting to the container directly.
{
"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; the gateway throttled the request before dispatch.
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.
Returns the current CAS revision token of the todo store (todos.revision) for optimistic concurrency on updates. Pass the returned revision on PATCH /todos/{id} to guard against concurrent updates.
Files a new master todo (todos.create). Deterministic triage (normalize, fingerprint-dedupe, defaults) runs server-side. The todo’s working directory is taken from the body cwd OR, if omitted, the X-Hoody-Cwd request-scope header; one of the two is required (todos.create rejects an empty cwd).
Optional priority band 0..4 (0 = P0 urgent … 4 = P4 someday); defaults to 2. The daemon reads a JSON number — a string value is NOT accepted (it silently defaults), so send an integer.
tags
array
No
Optional tags.
cwd
string
No
The todo’s working directory (labels the record’s computer/path). Defaults to the X-Hoody-Cwd request-scope header when omitted; one of the two must be set.
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, which has no realm dimension to scope.
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)
The request did not arrive through hoody-proxy.
Reach the agent through hoody-proxy.
{
"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.
{
"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 — a well-formed-but-large body is a size violation, not a JSON syntax error.
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; the gateway throttled the request before dispatch.
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).
Applies a CAS-guarded field patch / state transition to a todo (todos.update). Pass the current revision; a stale revision is rejected with 409 todo_conflict and you must refetch via getTodosRevision and retry.
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.archive RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch.
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.claim RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch.
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.release RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch.
Dispatches a background worker to autonomously work a todo (todos.run) and returns {job_id, session_id}. The daemon treats reaching the todos.run RPC as the human approval itself (issued by the TUI Run action or the CLI, both human-driven) — it has NO _machine_confirmed gate; that denial lives only on the model-facing run_todo tool. The minted job is bound to the worker session_id so its {job_id}→status lifecycle completes when the worker turn ends. Observe progress on the todo worker’s stream.
This is a J-class op: it spends model budget on an autonomous LLM run with no _machine_confirmed gate on the daemon RPC. The daemon’s own systemAdminGate may still 403 admin_unauthorized when a Unix-socket admin capability is configured.
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.run RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch.
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, which has no realm dimension to scope.
Omit the realm header on this route, or open a session to scope by realm.
{
"code": "admin_unauthorized",
"message": "memory administration requires a valid admin token"
}
Error Code
Title
Description
Resolution
admin_unauthorized
Rejected by the daemon’s own admin gate
The daemon’s Unix-socket memAdminGate / systemAdminGate refused the request. The HTTP edge has no service-level auth (hoody-proxy is the boundary, kit network-position trust), but the daemon retains its OWN socket-side admin gate: when an admin capability is configured on the daemon WITHOUT a matching forwarded admin_token, the daemon refuses. In the standard HTTP deployment the daemon runs with socket auth and no admin capability, so this gate allows the request and this code is not emitted.
Run the daemon with socket auth (--auth-token-path) and no separate admin capability so its own gate allows HTTP requests forwarded from the gateway; or, for the interactive TUI Memory tab over the socket, present the daemon’s configured Memory-admin token.
forbidden
Forbidden (not via the Hoody proxy)
The request did not arrive through hoody-proxy.
Reach the agent through hoody-proxy.
{
"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.
{
"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).
Reduce the request body below the configured limit (default 8 MiB).
{
"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; the gateway throttled the request before dispatch.
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).
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.cancel_run RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch.
Kicks an LLM triage pass over the inbox (todos.triage) and returns {job_id}. This is a J-class op: it spends model budget on an autonomous LLM run with no _machine_confirmed gate on the daemon RPC — the same property that makes todos.run a privileged op. Optional triage scoping in the body; an empty body triages the whole inbox.
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.triage RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch. An empty body triages the whole inbox.
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, which has no realm dimension to scope.
Omit the realm header on this route, or open a session to scope by realm.
{
"code": "admin_unauthorized",
"message": "memory administration requires a valid admin token"
}
Error Code
Title
Description
Resolution
admin_unauthorized
Rejected by the daemon’s own admin gate
The daemon’s Unix-socket memAdminGate / systemAdminGate refused the request. The HTTP edge has no service-level auth (hoody-proxy is the boundary, kit network-position trust), but the daemon retains its OWN socket-side admin gate: when an admin capability is configured on the daemon WITHOUT a matching forwarded admin_token, the daemon refuses. In the standard HTTP deployment the daemon runs with socket auth and no admin capability, so this gate allows the request and this code is not emitted.
Run the daemon with socket auth (--auth-token-path) and no separate admin capability so its own gate allows HTTP requests forwarded from the gateway; or, for the interactive TUI Memory tab over the socket, present the daemon’s configured Memory-admin token.
forbidden
Forbidden (not via the Hoody proxy)
The request did not arrive through hoody-proxy.
Reach the agent through hoody-proxy.
{
"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.
{
"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).
Reduce the request body below the configured limit (default 8 MiB).
{
"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; the gateway throttled the request before dispatch.
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).
Approves a run proposal on a todo (todos.approve_proposal). Approval is NOT inert: the daemon hands the approved proposal to startApprovedProposal, which spawns a background worker session equivalent to todos.run — a J-class autonomous run with no _machine_confirmed gate on the daemon RPC. The paired denyTodoProposal spawns no worker.
The HTTP edge has no service-level auth (hoody-proxy is the boundary, kit network-position trust); the daemon’s own systemAdminGate may still 403 admin_unauthorized when a Unix-socket admin capability is configured.
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.approve_proposal RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch. The todo {id} and proposal {pid} come from the path.
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, which has no realm dimension to scope.
Omit the realm header on this route, or open a session to scope by realm.
{
"code": "admin_unauthorized",
"message": "memory administration requires a valid admin token"
}
Error Code
Title
Description
Resolution
admin_unauthorized
Rejected by the daemon’s own admin gate
The daemon’s Unix-socket memAdminGate / systemAdminGate refused the request. The HTTP edge has no service-level auth (hoody-proxy is the boundary, kit network-position trust), but the daemon retains its OWN socket-side admin gate: when an admin capability is configured on the daemon WITHOUT a matching forwarded admin_token, the daemon refuses. In the standard HTTP deployment the daemon runs with socket auth and no admin capability, so this gate allows the request and this code is not emitted.
Run the daemon with socket auth (--auth-token-path) and no separate admin capability so its own gate allows HTTP requests forwarded from the gateway; or, for the interactive TUI Memory tab over the socket, present the daemon’s configured Memory-admin token.
forbidden
Forbidden (not via the Hoody proxy)
The request did not arrive through hoody-proxy.
Reach the agent through hoody-proxy.
{
"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.
{
"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).
Reduce the request body below the configured limit (default 8 MiB).
{
"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; the gateway throttled the request before dispatch.
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).
Honor Retry-After and retry.
POST /api/v1/agent/todos/{id}/proposals/{pid}/deny
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.deny_proposal RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch. The todo {id} and proposal {pid} come from the path.
Permanently and irreversibly removes archived todos (todos.purge) — the tombstones are destroyed with no _machine_confirmed gate on the daemon RPC. This is a J-class op on the same axis as run and triage: it can spend budget on autonomous activity. The daemon’s own systemAdminGate may still 403 admin_unauthorized when a Unix-socket admin capability is configured. Optional filters in the body scope which archived todos to purge; an empty body purges all archived tombstones for the cwd.
Per-request realm selector (§6.1): "global" or a 24-hex id.
realm
query
string
No
Per-request realm selector (§6.1) — the in:query alias of X-Hoody-Realm.
This endpoint accepts an optional JSON body. The body is forwarded to the daemon todos.purge RPC; the gateway scrubs every _-prefixed key and folds in the request scope (cwd, config_dir) before dispatch. An empty body purges all archived tombstones for the cwd.
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, which has no realm dimension to scope.
Omit the realm header on this route, or open a session to scope by realm.
{
"code": "admin_unauthorized",
"message": "memory administration requires a valid admin token"
}
Error Code
Title
Description
Resolution
admin_unauthorized
Rejected by the daemon’s own admin gate
The daemon’s Unix-socket memAdminGate / systemAdminGate refused the request. The HTTP edge has no service-level auth (hoody-proxy is the boundary, kit network-position trust), but the daemon retains its OWN socket-side admin gate: when an admin capability is configured on the daemon WITHOUT a matching forwarded admin_token, the daemon refuses. In the standard HTTP deployment the daemon runs with socket auth and no admin capability, so this gate allows the request and this code is not emitted.
Run the daemon with socket auth (--auth-token-path) and no separate admin capability so its own gate allows HTTP requests forwarded from the gateway; or, for the interactive TUI Memory tab over the socket, present the daemon’s configured Memory-admin token.
forbidden
Forbidden (not via the Hoody proxy)
The request did not arrive through hoody-proxy.
Reach the agent through hoody-proxy.
{
"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.
{
"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).
Reduce the request body below the configured limit (default 8 MiB).
{
"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; the gateway throttled the request before dispatch.
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).