Agent: Sessions
Section titled “Agent: Sessions”A session is the long-lived context in which an agent runs: an attached conversation with a frozen binding (realm, container, working directory, tool mode, directory scope, backend) and a persisted history. This page is the entry point for the session lifecycle: listing persisted sessions, reading one, reading its per-call LLM usage, listing the directories it spans, creating or forking or attaching one, renaming it, closing it, trimming its history, and hard-deleting it. Turn dispatch, streaming, gates, leases, and per-session settings each have their own page.
Session lifecycle overview
Section titled “Session lifecycle overview”- Create with
POST /api/v1/agent/sessions. Use theforkfield to branch from an existing session, or theattachfield to rejoin one. Send anIdempotency-Keyheader to make a create retry-safe: a retry with the same key re-joins the same session (replayed:true). - Read the persisted summary with
GET /api/v1/agent/sessions/{id}. Live sessions also report their frozen start binding and any parked gate. - Read LLM usage with
GET /api/v1/agent/sessions/{id}/usageto walk the per-call usage log for a live or closed session without attaching. - Rename with
PATCH /api/v1/agent/sessions/{id}to set or clear the title. - Close the live connection with
POST /api/v1/agent/sessions/{id}/close. To also remove the persisted record, useDELETE /api/v1/agent/sessions/{id}?hard=trueinstead. - Trim the conversation history with
POST /api/v1/agent/sessions/{id}/trim.
List sessions
Section titled “List sessions”GET /api/v1/agent/sessions
Section titled “GET /api/v1/agent/sessions”Lists persisted sessions for the requesting cwd (open and closed), with an Attached flag set for live sessions. Pass ?page=N&limit=M to receive the page-th window of size M (page is 1-based; an invalid page or limit returns 400 bad_request). Omit limit to receive the full set. meta.total is always the full pre-slice count; meta.page and meta.limit echo the window only when a limit was supplied.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
include_system | query | boolean | No | When true, also include daemon-owned system/resident sessions in the listing. |
page | query | integer | No | 1-based page number for pagination. |
limit | query | integer | No | Maximum items per page (0 = no pagination). |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
realm | query | string | No | The realm to list: “global” (sessions not tied to a realm), a 24-hex realm id, or “all” for every realm this login serves. Also accepted as the X-Hoody-Realm header, except “all”. Omitted, the agent’s current realm. |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
This endpoint takes no request body.
Example
Section titled “Example”curl -X GET \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions?page=1&limit=20' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
for await (const item of client.agent.sessions.listIterator({ page: 1, limit: 20 })) { console.log(item);}{ "items": [ { "agent": "general", "attached": true, "attached_clients": 1, "cwd": "/home/user/projects/my-app", "dir_scope": "workspace", "dir_scope_locked": true, "first_message": "Walk through the release checklist.", "id": "9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55", "last_request_at": "2026-09-28T10:15:04Z", "model": "anthropic/claude-sonnet-4-6", "name": "Release checklist", "realm": "global", "realm_selector": "global", "started_at": "2026-09-28T09:12:44Z", "tool_mode": "standard", "tool_mode_locked": true, "total_cost_usd": 0.42 } ], "meta": { "total": 1 }}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | 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 or 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. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "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. |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
restriction_unknown | Login restriction unknown | The agent cannot read yet which realms its login may serve (GET /hoody/auth/status reports restriction “unknown”), so it refuses realm-scoped requests until it can. Nothing was read or changed. | Retry after the restriction is readable. |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
Get a session summary
Section titled “Get a session summary”GET /api/v1/agent/sessions/{id}
Section titled “GET /api/v1/agent/sessions/{id}”Returns the persisted summary for a session (live or closed). A live session also surfaces its frozen start binding (started: realm, container, modes), attached:true, its incarnation (the namespace of its stream sequence numbers and gate ids), and pending_gate {id, generation, type} while a confirm or question gate is parked.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | The session id. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | Per-request realm selector, the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
This endpoint takes no request body.
Example
Section titled “Example”curl -X GET \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.sessions.get('9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55');{ "session": { "agent": "general", "attached": true, "attached_clients": 1, "cwd": "/home/user/projects/my-app", "dir_scope": "workspace", "dir_scope_locked": true, "first_message": "Walk through the release checklist.", "id": "9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55", "last_request_at": "2026-09-28T10:15:04Z", "model": "anthropic/claude-sonnet-4-6", "name": "Release checklist", "realm_selector": "global", "started_at": "2026-09-28T09:12:44Z", "tool_mode": "standard", "tool_mode_locked": true, "total_cost_usd": 0.42 }, "attached": true, "started": { "realm_selector": "global", "container_id": "67e89abc123def456789abcd-890abcdef12345678901cdef", "cwd": "/home/user/projects/my-app", "tool_mode": "standard", "dir_scope": "workspace", "backend": "" }, "incarnation": "inc_9f2c4e1a5b7d4c3e8a216d0f3b9e7c55"}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | 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 or 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. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "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": "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. |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
restriction_unknown | Login restriction unknown | The agent cannot read yet which realms its login may serve (GET /hoody/auth/status reports restriction “unknown”), so it refuses realm-scoped requests until it can. Nothing was read or changed. | Retry after the restriction is readable. |
Read session LLM usage
Section titled “Read session LLM usage”GET /api/v1/agent/sessions/{id}/usage
Section titled “GET /api/v1/agent/sessions/{id}/usage”Returns one row per recorded LLM call of the session (its turns, and the helpers and workflow steps it ran) with the token counts the provider reported (cache reads and cache writes apart from uncached input), the cost and the latency, oldest first, plus totals over the whole session. Use it to see how much of each prompt the provider served from its cache. A failed attempt is its own row. Works for a live or closed session without attaching.
after_id is an exclusive cursor: rows with a larger id. To read every row, send next_after_id back as after_id while has_more is true; polling with next_after_id later returns only rows written since. limit caps the page (1 to 5000, default 500). totals always cover every row of the session, whatever page was asked for. A session that is not visible to this caller is a plain 404 (uniform envelope).
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | The session id. |
after_id | query | integer | No | Exclusive cursor: return rows whose id is greater than this. Omit (or 0) to start from the session’s first row. Negative or non-integer = 400. |
limit | query | integer | No | Return at most this many rows (1 to 5000, default 500). Out of range or non-integer = 400. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | Per-request realm selector, the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
This endpoint takes no request body.
Example
Section titled “Example”curl -X GET \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55/usage?after_id=0&limit=500' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
for await (const item of client.agent.sessions.getUsageIterator('9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55', { after_id: 0, limit: 500 })) { console.log(item);}{ "has_more": false, "items": [ { "attempt": -1, "cache_read_tokens": 14200, "cache_write_tokens": 0, "cost_usd": 0, "error_code": "", "id": 412, "input_tokens": 180, "latency_ms": 2140, "model": "hoody-ai/hoody-free", "output_tokens": 95, "provider": "hoody-ai", "success": true, "ts": 1760000000, "turn_id": "3f2a9c1e5b07" } ], "next_after_id": 412, "totals": { "cache_read_pct": 98.75, "cache_read_tokens": 14200, "cache_write_tokens": 0, "calls": 1, "cost_usd": 0, "input_tokens": 180, "output_tokens": 95 }}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | 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. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "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": "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. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked or written, so the operation did not complete. The message names the store (for example “the agent’s settings file is damaged and was left unchanged”). A damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. The agent’s log (GET /logs) records a “store failure” entry with the store, the failure kind (io, corrupt or lock) and a path-free cause such as “open: permission denied” or “invalid JSON at offset 12”; it does not name the file’s path. |
{ "code": "restriction_unknown", "message": "the login's realm restriction could not be read — retry"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
restriction_unknown | Login restriction unknown | The agent cannot read yet which realms its login may serve (GET /hoody/auth/status reports restriction “unknown”), so it refuses realm-scoped requests until it can. Nothing was read or changed. | Retry after the restriction is readable. |
List distinct session working directories
Section titled “List distinct session working directories”GET /api/v1/agent/sessions/cwds
Section titled “GET /api/v1/agent/sessions/cwds”Returns the distinct working directories across all persisted sessions, most-recent first.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | Per-request realm selector, the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
This endpoint takes no request body.
Example
Section titled “Example”curl -X GET \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/cwds' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.sessions.listDirectories();{ "cwds": [ "/home/user/projects/parser", "/home/user/projects/ci", "/home/user/scratch" ]}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | 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 or 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. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "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. |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
restriction_unknown | Login restriction unknown | The agent cannot read yet which realms its login may serve (GET /hoody/auth/status reports restriction “unknown”), so it refuses realm-scoped requests until it can. Nothing was read or changed. | Retry after the restriction is readable. |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
Create, fork, or attach a session
Section titled “Create, fork, or attach a session”POST /api/v1/agent/sessions
Section titled “POST /api/v1/agent/sessions”Opens a new agent session, or forks or attaches an existing one. The session binding is frozen at start: realm, container, cwd, tool_mode, dir_scope, and backend cannot be changed afterward.
- To create a new session, omit
attachandfork. - To fork from an existing session, supply
fork(the parent session id), and optionallyfork_turn_idxto choose where to branch. The fork inherits the parent’s chat agent. - To attach to an existing session, supply
attach(the session id). A resumed session keeps its model and chat agent.
BYOA: backend:"acp" plus delegated_agent delegate the session to an external CLI agent (the only supported BYOA backend is "claude"); the daemon enforces the per-agent enable gate (the local sandbox is mandatory; there is no trust gate).
Send a standard Idempotency-Key header to make the create retry-safe: a retry with the same key re-joins the same session (replayed:true) instead of creating a second one. The same key with a different request body returns 422.
A session that has never been prompted is a draft and is not saved: once no client has been attached to it for 5 minutes it is closed and discarded, and later calls by its id answer 404. Send the first prompt, or keep a stream or an attachment lease open, to keep it. A session with at least one turn is saved and can be re-attached by id after it is closed.
For a keyed create, every start refusal except admission_write_failed and session_terminating consumes the key: create again with a new Idempotency-Key.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
Idempotency-Key | header | string | No | Opaque retry key (1 to 255 printable ASCII, no whitespace). A retry with the same key returns the same receipt and never re-runs the turn; the same key with a different request body is 422. |
realm | query | string | No | Per-request realm selector (the query alias of the X-Hoody-Realm header, read only when the header is absent). "global" or a 24-hex id. Rejected on active-only routes. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected on routes with no container dimension. |
X-Hoody-Realm | header | string | No | Per-request realm selector: "global" or a 24-hex id. Rejected on active-only routes. |
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
realm | string | No | Realm selector to scope the session (frozen at start). |
container | string | No | Container id or selector to run tools on (frozen at start). |
cwd | string | No | Working directory for the session (frozen at start). |
config_dir | string | No | Config directory override. |
model | string | No | Model for this session. On a fresh create or fork it overrides the chat agent’s pinned model for this session only (it is not written to the agent; use PATCH /sessions/{id}/model to change it and repin the agent globally). Omit it to use the agent’s pinned model (or your default). A model with no known provider prefix, or a fusion composite that does not exist, is refused 422 unknown_model and no session is created (client.agent.models.list() lists the choices). Rejected (400) together with attach (a resumed session keeps its model) or backend:"acp" (the delegated agent selects its own). |
agent | string | No | Initial chat-agent name. Rejected (400) together with fork (a fork inherits its parent’s chat agent) or attach (a resumed session keeps its agent). |
tool_mode | string | No | Initial tool mode (frozen at start): standard (the default) or orchestrator. Any other value is refused 400 invalid_tool_mode. |
dir_scope | string | No | Initial directory-access scope (frozen at start): home or full. Any other value is refused 400 invalid_dir_scope. |
attach | string | No | Attach to an existing session instead of creating one; the value is that session’s id. |
fork | string | No | Fork from an existing session; the value is that session’s id. The source must be LIVE in the daemon: fork copies its in-memory state and does not load a saved record the way attach does. An unknown id, a session in another realm, or one that has ended (closed, reaped or lost to a restart) is refused 404 not_found and no session is created; attach the ended session first to reload it, then fork it. |
fork_turn_idx | integer | No | Turn index to fork at (with fork). |
backend | string | No | Session backend: "" (Hoody LLM) or "acp" (BYOA delegated agent). |
delegated_agent | string | No | BYOA agent when backend:"acp": claude. |
headless | boolean | No | Start the session in headless posture. |
approval | object | No | Approval policy assertion. On a create or fork it sets the session’s policy; on an attach it is checked against the live session (a session that cannot honour it returns 409 approval_policy_unsatisfiable, and it is closed for this caller). Asserting "always" also hands this caller the approver lease when none is live (approver_lease in the response, returned once). See the table below. |
expected_binding | object | No | Fail-closed binding assertion. Only the dimensions given are compared with the binding the session actually resolved to; a contradiction returns 409 binding_mismatch (details name each mismatch) and the session is closed. See the table below. |
prompt_blocks | object | No | Switches for the optional system-prompt blocks, for a lean session: false turns a block off for this session. Switches only turn blocks off: true undoes a false in the agent’s own prompt_blocks frontmatter, but never turns on a block the box settings turned off. An omitted key keeps the block. Frozen at create and kept on resume; a fork inherits its parent’s switches and applies its own on top. An unknown key is 400; rejected (400) together with attach. See the table below. |
model_settings | object | No | Per-turn model settings for this session, the same object as the completion route’s settings: thinking, temperature, max_tokens, response_format. Per field they win over the chat agent’s own frontmatter; an omitted field keeps the agent’s value. Every turn applies them leniently: a setting the model or its adapter cannot take is left out of that request and logged, never an error. Frozen at create and kept on resume; a fork inherits its parent’s settings and applies its own on top. Unknown or invalid members are 400; rejected (400) together with attach, with backend:"acp", and on a fork of a delegated (acp) session that names no backend. Subagents and workflow steps do not inherit them. See the table below. |
helper_gates | boolean | No | true parks a spawn_agent helper’s approvals (a hook asking to confirm) and questions as gates on this session instead of failing them. Each is listed under pending_helper_gates and answered by its gate_id on /confirm or /answer, beside the lead’s own gate. Fixed when the session starts: set it on the create, or on an attach that reopens a closed session. On an attach to a live session, true for a session started without it is refused 400 helper_gates_unavailable. Defaults to false. |
outcome_claims | boolean | No | true gives the model the report_outcome tool and one prompt line asking it to call the tool last, alone, to say what it did and cite the tool calls that show it. A response whose every tool call is an accepted report_outcome ends the turn through the same end-of-turn checks as a normal finish. The turn then sends event.outcome_claims just before event.agent_done. No effect on backend:"acp". Fixed when the session starts, like helper_gates; on an attach to a live session, true for a session started without it is refused 400 outcome_claims_unavailable. Defaults to false. |
frame_tools | boolean | No | true gives the model the request_view tool and one prompt line asking it to use the tool when the user asks for a View: a live page of their data, such as a table of their containers. Set it only from a Frame that answers it. A call parks as a question gate (event.user_question with category frame_request and frame_request {kind: "view", request, id}); the Frame makes the View and answers on /answer with its status JSON. Never shown to a headless, delegated, orchestrator, or resident session; an automated turn (a loop, TODO, or background-task fire) is refused it. Fixed when the session starts, like helper_gates; on an attach to a live session, true for a session started without it is refused 400 frame_tools_unavailable. Defaults to false. |
approval object
Section titled “approval object”| Field | Type | Required | Description |
|---|---|---|---|
mode | string | No | The approval mode to assert: "" (default policy), default, or always (every tool call waits for the approver). An empty string means default, exactly like omitting the property; the value is compared after surrounding whitespace is trimmed. |
locked | boolean | No | Lock the policy so later setters (PUT /sessions/{id}/approval, PATCH /sessions/{id}/yolo) are refused. |
expected_binding object
Section titled “expected_binding object”| Field | Type | Required | Description |
|---|---|---|---|
realm | string | No | The realm selector the session must have resolved to. |
container | string | No | The container id the session must have resolved to. |
cwd | string | No | The working directory the session must have resolved to. |
backend | string | No | The backend the session must be running on: "" (unasserted), llm (Hoody LLM), or acp (BYOA delegated agent). An empty string leaves this dimension unasserted, exactly like omitting the property. |
prompt_blocks object
Section titled “prompt_blocks object”| Field | Type | Required | Description |
|---|---|---|---|
contract | boolean | No | The task-completion contract (finish the task, verify results, say what remains). Callers that read a session’s outcome rely on it; turn it off only when you handle outcome reporting yourself. |
transcripts | boolean | No | Where this realm’s other session transcripts are stored. |
frequent_files | boolean | No | The list of files the agent opens most often in this project. |
hook_context | boolean | No | Context returned by SessionStart hooks. |
memory | boolean | No | Project memory recalled for this session. |
project_instructions | boolean | No | Project instruction files (CLAUDE.md, AGENTS.md). |
agent_instructions | boolean | No | The user’s global instructions for every agent. |
team_rules | boolean | No | The team rules (guidance and playbook rules from client.agent.hooks.setRules()) that cover the agent. |
skills | boolean | No | The catalogue of available skills. |
workflows | boolean | No | The catalogue of available workflows and the workflow hint. |
verbosity | boolean | No | The response-verbosity directive. |
hoody_platform | boolean | No | The Hoody platform briefing. |
hoody_exec | boolean | No | The hoody-exec script conventions. |
fleet_notice | boolean | No | The notice that fleet tools are unavailable in this session. |
model_settings object
Section titled “model_settings object”| Field | Type | Required | Description |
|---|---|---|---|
thinking | object | No | Reasoning: {type: enabled or disabled, effort?: low, medium, high, xhigh, or max}. effort is allowed only with enabled. Absent keeps the model’s default reasoning. |
temperature | number | No | Sampling temperature, 0 to 2 (the model’s own range applies; many models accept it only with thinking disabled). |
max_tokens | integer | No | Output cap, 1 to 1000000, clamped to the model’s output limit. |
response_format | object | No | {type: json_object}, or {type: json_schema, schema: {...}, name?, strict?}. name matches ^[A-Za-z0-9_-]{1,64}$ and defaults to "response". |
Example
Section titled “Example”curl -X POST \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: 01HF8Z2P3QYVJ4N5K6M7R8T9C0' \ -d '{ "cwd": "/home/user/projects/parser", "tool_mode": "standard", "dir_scope": "home", "headless": false, "approval": { "mode": "default", "locked": false }, "model_settings": { "thinking": { "type": "enabled", "effort": "medium" } } }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.sessions.create( { cwd: '/home/user/projects/parser', tool_mode: 'standard', dir_scope: 'home', headless: false, approval: { mode: 'default', locked: false }, model_settings: { thinking: { type: 'enabled', effort: 'medium' } } }, { IdempotencyKey: '01HF8Z2P3QYVJ4N5K6M7R8T9C0' });{ "session_id": "9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55", "started": { "realm_selector": "global", "container_id": "67e89abc123def456789abcd-890abcdef12345678901cdef", "cwd": "/home/user/projects/parser", "tool_mode": "standard", "dir_scope": "home", "backend": "" }, "approval": { "mode": "default", "locked": false, "revision": 1 }, "replayed": false}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_blocked | Realm blocked | The requested realm is blocked or corrupt; the request fails closed before dispatch. | Select a valid, reachable realm. |
realm_corrupt | Realm record corrupt | The attach target’s persisted realm binding is corrupt; the session cannot be re-bound and the request fails closed. | Recreate the session in a valid realm. |
realm_conflict | Realm conflict | The supplied realm conflicts with the parent’s (fork) or the session’s frozen (attach) realm binding. | Omit the realm header to inherit, or match the existing binding. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not "", "global", or a 24-hex id). | Pass a valid realm selector. |
container_conflict | Container conflict | The supplied container conflicts with the parent’s (fork) or the session’s frozen (attach) container binding. | Omit the container header to inherit, or match the existing binding. |
delegated_agent_invalid | Delegated agent invalid | The delegated_agent value (for backend:"acp") is not a known BYOA backend. | Use "claude", the only BYOA backend. |
helper_gates_unavailable | Helper gates unavailable | helper_gates:true was sent to attach to a live session that was started without it. helper_gates is fixed when a session starts. | Attach without helper_gates, or send helper_gates:true on the create, or on an attach after the session has closed. |
outcome_claims_unavailable | Outcome claims unavailable | outcome_claims:true was sent to attach to a live session that was started without it. outcome_claims is fixed when a session starts. | Attach without outcome_claims, or send outcome_claims:true on the create, or on an attach after the session has closed. |
frame_tools_unavailable | Frame tools unavailable | frame_tools:true was sent to attach to a live session that was started without it. frame_tools is fixed when a session starts. | Attach without frame_tools, or send frame_tools:true on the create, or on an attach after the session has closed. |
invalid_tool_mode | Invalid tool mode | tool_mode is not standard or orchestrator. Nothing was started. details.field is “tool_mode”. | Send standard or orchestrator, or omit tool_mode. |
invalid_dir_scope | Invalid directory scope | dir_scope is not home or full. Nothing was started. details.field is “dir_scope”. | Send home or full, or omit dir_scope. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
realm_not_allowed | Realm not allowed for this login | The agent is logged in with a token limited to some realms (GET /hoody/auth/status lists them in realm_ids), and the session’s realm is not one of them, or is the global scope. Nothing was started. | Change the request, then create again with a new Idempotency-Key; the old key will not start a new session. On an attach (no key is reserved), correct the request and attach again. |
{ "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": "session_busy", "message": "session is already live in another connection"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
session_busy | Session busy | The session is already live in another connection (single-writer-per-session). | Close the other attach, or attach a fork instead. |
session_not_attachable | Session not attachable | The attach target is a daemon-managed session (for example a todo worker) and cannot be attached over HTTP. | Attach a user session, or fork the target instead of attaching it. |
approval_policy_unsatisfiable | Approval policy unsatisfiable | The approval assertion cannot be honoured: the backend cannot park on a decision (delegated/acp), the session’s posture is already open (automatic write permission), or an attach asserted a posture the shared session does not have. Nothing was started. | Change the request, then create again with a new Idempotency-Key; the old key will not start a new session. On an attach (no key is reserved), correct the request and attach again. |
approval_policy_locked | Approval policy locked | A fork asserted a weaker posture than its parent’s locked approval policy; a locked policy is never weakened. Nothing was started. | Change the request, then create again with a new Idempotency-Key; the old key will not start a new session. On an attach (no key is reserved), correct the request and attach again. |
binding_mismatch | Binding mismatch | The session would resolve to a realm, container, cwd, or backend that contradicts the expected_binding assertion; it was refused before anything ran under the wrong binding. details carries {expected, resolved} when the gateway detected it. | Change the request, then create again with a new Idempotency-Key; the old key will not start a new session. On an attach (no key is reserved), correct the request and attach again. |
binding_unverifiable | Binding unverifiable | expected_binding.cwd was asserted on a REMOTE delegated (ACP) session, whose working directory cannot be verified before the backend connects; the start is refused rather than started under an unverified binding. | Change the request, then create again with a new Idempotency-Key; the old key will not start a new session. On an attach (no key is reserved), correct the request and attach again. |
creation_interrupted | Creation interrupted | An earlier create with this Idempotency-Key reserved a session id but the daemon never acknowledged it (it was interrupted). The daemon refuses rather than allocate a second session. | List sessions to find it, or create with a fresh Idempotency-Key. |
approver_lease_invalid | Approver lease not issued | The session could not be started or joined: its approver lease could not be issued. | On a create: create again, with a new Idempotency-Key if you sent one; the old key will not start a new session. On an attach, or a keyed create that re-joined the session its key started: send the same request again. |
{ "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": "cwd_not_found", "message": "working directory does not exist"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
cwd_not_found | Working directory not found | The requested working directory does not exist on the daemon host (validated at session start for a self-bound session). | Create the directory or pass an existing cwd. |
container_gone | Bound container no longer exists | The session’s frozen REMOTE container no longer exists in its realm (validated at session start). | Start a new session and pick another container (or this computer). |
acp_container_unsupported | Delegated session cannot bind this container | A delegated (ACP) session was bound to a container this box can’t run ACP agents in. | Omit container_id to run the agent locally, or enable the Hoody integration. |
idempotency_key_reused | Idempotency key reused | The Idempotency-Key was already used to create a session from a DIFFERENT start body. Nothing was created. | Use a fresh Idempotency-Key for a different session, or resend the identical body to re-join the session the key created. |
unknown_model | Unknown model | The model has no known provider prefix, or names a fusion composite that does not exist. No session was created. | Pick a model from GET /api/v1/agent/models, or omit model to use the agent’s own. |
{ "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. |
admission_write_failed | Create ledger write failed | The daemon could not durably reserve the Idempotency-Key, so no session was created (fail-closed: nothing is created unrecorded). The same code answers a retry while the key’s first create is still in progress after about 30 seconds: the key stays reserved, and that create may still start its session. | Retry with the SAME Idempotency-Key: once the key’s first create finishes, the retry re-joins its session if it started, or answers 409 creation_interrupted if it did not (then use a NEW Idempotency-Key). If it persists, check the daemon’s session store (disk / permissions). |
policy_commit_failed | Policy commit failed | The session’s approval policy could not be written durably, so the session was not started. Its Idempotency-Key was already reserved. | Safe to retry. A keyed create must use a NEW Idempotency-Key: the old one now answers 409 creation_interrupted. If it persists, check the daemon’s session store (disk / permissions). |
session_terminating | Session terminating | The session is shutting down: a concurrent open lost a race with its teardown, or a hard delete waited for the teardown past its bound. Nothing was opened or deleted. | Retry shortly. |
restriction_unknown | Login restriction unknown | The agent cannot read yet which realms its login may serve (GET /hoody/auth/status reports restriction “unknown”), so it starts no session until it can. Nothing was started. | Retry after the restriction is readable, with a new Idempotency-Key; the old key can only answer 409 creation_interrupted or 422 idempotency_key_reused. |
Rename a session
Section titled “Rename a session”PATCH /api/v1/agent/sessions/{id}
Section titled “PATCH /api/v1/agent/sessions/{id}”Sets the session’s title, or clears it back to the automatic title with name:"". Works on a live session (renamed in place; attached clients receive event.session_renamed) and on a closed one (its persisted record is rewritten where it lies; a closed session stays closed). The daemon sanitizes the title (control characters removed, surrounding whitespace trimmed, secret-shaped tokens redacted, capped at 120 characters) and returns the value it stored.
A live session that has not run a turn yet keeps the title in memory and persists it with its first turn. name is required: an absent field is 400, never a silent clear. A session in another realm, owned by another account, managed by the agent itself, or simply unknown is the same plain 404.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | The session id. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | Per-request realm selector, the in:query alias of the X-Hoody-Realm header (read only when the header is absent): “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in: a session in another realm, or in a realm this login does not serve, is 404 not_found. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The new title. An empty string clears it back to the automatic title. Required; omitting it is a 400, not a clear. |
Example
Section titled “Example”curl -X PATCH \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "name": "Release checklist" }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.sessions.rename('9f2c4e1a-5b7d-4c3e-8a21-6d0f3b9e7c55', { name: 'Release checklist' });{ "status": "ok", "name": "Release checklist"}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | 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 or 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. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "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. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked or written, so the operation did not complete. The message names the store (for example “the agent’s settings file is damaged and was left unchanged”). A damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. The agent’s log (GET /logs) records a “store failure” entry with the store, the failure kind (io, corrupt or lock) and a path-free cause such as “open: permission denied” or “invalid JSON at offset 12”; it does not name the file’s path. |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
restriction_unknown | Login restriction unknown | The agent cannot read yet which realms its login may serve (GET /hoody/auth/status reports restriction “unknown”), so it refuses realm-scoped requests until it can. Nothing was read or changed. | Retry after the restriction is readable. |
Close the session
Section titled “Close the session”POST /api/v1/agent/sessions/{id}/close
Section titled “POST /api/v1/agent/sessions/{id}/close”Tears the session down. The daemon cancels it and removes the live session. Use this when you want to end the connection but keep the persisted record. To also remove the persisted record, use DELETE /api/v1/agent/sessions/{id}?hard=true instead.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Session identifier. |
realm | query | string | No | Per-request realm selector (the query alias of the X-Hoody-Realm header, read only when the header is absent). "global" or a 24-hex id. Rejected on active-only routes. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected on routes with no container dimension. |
X-Hoody-Realm | header | string | No | Per-request realm selector: "global" or a 24-hex id. Rejected on active-only routes. |
This endpoint takes no request body.
Example
Section titled “Example”curl -X POST \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/sess_01HF8Z2P3QYVJ4N5K6M7R8T9C0/close' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.sessions.close('sess_01HF8Z2P3QYVJ4N5K6M7R8T9C0');{ "status": "ok"}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | 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 or 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. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "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). | Honor Retry-After and retry. |
Trim session history
Section titled “Trim session history”POST /api/v1/agent/sessions/{id}/trim
Section titled “POST /api/v1/agent/sessions/{id}/trim”Truncates the session’s conversation history to (and including) the given turn_idx. When a gate is parked, the command is applied only between turns, so it may not have taken effect yet (deferred:true, with a note explaining why).
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Session identifier. |
realm | query | string | No | Per-request realm selector (the query alias of the X-Hoody-Realm header, read only when the header is absent). "global" or a 24-hex id. Rejected on active-only routes. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected on routes with no container dimension. |
X-Hoody-Realm | header | string | No | Per-request realm selector: "global" or a 24-hex id. Rejected on active-only routes. |
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
turn_idx | integer | No | Turn index to truncate history to (and including). |
Example
Section titled “Example”curl -X POST \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/sess_01HF8Z2P3QYVJ4N5K6M7R8T9C0/trim' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "turn_idx": 4 }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.sessions.trim('sess_01HF8Z2P3QYVJ4N5K6M7R8T9C0', { turn_idx: 4 });{ "status": "ok", "deferred": false}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | 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 or 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. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "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). | Honor Retry-After and retry. |
Delete a session
Section titled “Delete a session”DELETE /api/v1/agent/sessions/{id}
Section titled “DELETE /api/v1/agent/sessions/{id}”Closes the live session connection (the daemon cancels the session). Pass ?hard=true to also remove the persisted record. Without ?hard, only the live connection is torn down.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Session identifier. |
hard | query | boolean | No | When true, also remove the persisted session record (not just the live connection). |
realm | query | string | No | Per-request realm selector (the query alias of the X-Hoody-Realm header, read only when the header is absent). "global" or a 24-hex id. Rejected on active-only routes. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk install a stateless read or write resolves. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected on routes with no container dimension. |
X-Hoody-Realm | header | string | No | Per-request realm selector: "global" or a 24-hex id. Rejected on active-only routes. |
This endpoint takes no request body.
Example
Section titled “Example”curl -X DELETE \ 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/sessions/sess_01HF8Z2P3QYVJ4N5K6M7R8T9C0?hard=true' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.sessions.delete('sess_01HF8Z2P3QYVJ4N5K6M7R8T9C0');{ "status": "ok", "closed": true, "hard": true}{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | 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 or 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. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not "", "global", or a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "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. |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
session_live | Session is live | A hard delete found the session attached by a client (another connection, or a re-attach after the close). Nothing was deleted. | Close the session (POST /sessions/{id}/close, or DELETE without ?hard), then retry the hard delete. |
{ "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. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked or written, so the operation did not complete. The message names the store (for example “the agent’s settings file is damaged and was left unchanged”). A damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. The agent’s log (GET /logs) records a “store failure” entry with the store, the failure kind (io, corrupt or lock) and a path-free cause such as “open: permission denied” or “invalid JSON at offset 12”; it does not name the file’s path. |
{ "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. |
session_terminating | Session terminating | The session is shutting down: a concurrent open lost a race with its teardown, or a hard delete waited for the teardown past its bound. Nothing was opened or deleted. | Retry shortly. |
restriction_unknown | Login restriction unknown | The agent cannot read yet which realms its login may serve (GET /hoody/auth/status reports restriction “unknown”), so it refuses realm-scoped requests until it can. Nothing was read or changed. | Retry after the restriction is readable. |