# Agent: Todos

**Page:** api/agent/todos

[Download Raw Markdown](./api/agent/todos.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



The Agent: Todos API manages the full lifecycle of master todos across file, read, claim, run, snooze, archive, comment, message, triage, and proposal routes. Use these endpoints to list and search todos, CAS-update fields, claim and release leases, dispatch autonomous runs, post comments and orchestrator-turn messages, approve or deny run proposals, and run an LLM triage pass over the inbox. All routes are scoped to a working directory and an optional container and realm. Every mutation that touches a specific todo accepts that todo's own `revision` (from `getTodo`) as a CAS guard, which is distinct from the store-wide poll cursor returned by `getTodosRevision`.

Base host for all routes: `https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu`

## List and read

### `GET /api/v1/agent/todos`

Lists master todos for the requesting cwd and config_dir. Returns the store-wide poll cursor `revision` (a change-detection cursor for the whole list — NOT the per-todo CAS token; `updateTodo`, `archive`, `snooze`, and `claim` take each todo's own `revision` from `getTodo`). 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.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `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: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

Optional typed filters forwarded to `todos.list`. All optional; omit the body for the full list.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `states` | array | No | Filter to these todo states (array of strings). |
| `tags` | array | No | Filter to todos carrying these tags (array of strings). |
| `query` | string | No | Free-text filter over title/body. |
| `open_only` | boolean | No | When true, only open (non-terminal) todos. |
| `all` | boolean | No | When true, include archived/closed todos. |

```json
{
  "states": ["open", "queued"],
  "tags": ["backend", "p1"],
  "open_only": true
}
```



```bash
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos?page=1&limit=20" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{"open_only": true, "tags": ["p1"]}'
```


```ts
await client.agent.todos.listTodosIterator(
  { open_only: true, tags: ["p1"] },
  { page: 1, limit: 20, "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "items": [
    {
      "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
      "title": "Refactor auth middleware",
      "state": "open",
      "priority": 1,
      "tags": ["backend", "p1"],
      "revision": 7,
      "created_at": "2026-04-12T09:15:24Z"
    }
  ],
  "meta": {
    "total": 42,
    "page": 1,
    "limit": 20
  }
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |



### `GET /api/v1/agent/todos/{id}`

Returns the full todo record including timeline + proposals (`todos.read`).

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

This endpoint takes no request body.



```bash
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT" \
  -H "X-Hoody-Cwd: /home/user/project"
```


```ts
await client.agent.todos.getTodo("t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT", {
  "X-Hoody-Cwd": "/home/user/project"
});
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "title": "Refactor auth middleware",
  "body": "Move all session parsing out of the request handler.",
  "state": "open",
  "priority": 1,
  "tags": ["backend", "p1"],
  "revision": 7,
  "created_at": "2026-04-12T09:15:24Z",
  "timeline": [],
  "proposals": []
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |



### `GET /api/v1/agent/todos/revision`

Returns the store-wide revision counter (`todos.revision`) — a cheap poll cursor that moves on every persisted mutation, for change detection over the whole list. NOT the update CAS token: `updateTodo`, `archive`, `snooze`, and `claim` take the individual todo's own `revision` from `getTodo`.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

This endpoint takes no request body.



```bash
curl -X GET "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/revision" \
  -H "X-Hoody-Cwd: /home/user/project"
```


```ts
await client.agent.todos.getTodosRevision({
  "X-Hoody-Cwd": "/home/user/project"
});
```


```json
{
  "revision": 184
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |



## File and update

### `POST /api/v1/agent/todos`

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).

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

New todo (forwarded to `todos.create`; deterministic triage runs server-side).

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | Yes | Todo title. |
| `body` | string | No | Todo body / description. |
| `priority` | integer | No | Optional priority band 0..4 (0 = P0 urgent … 4 = P4 someday); defaults to 2 when omitted. Must be a JSON integer in range — a string or out-of-range value is rejected. |
| `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. |

```json
{
  "title": "Add request-id middleware",
  "body": "Stamp every inbound request with an X-Request-Id header for tracing.",
  "priority": 2,
  "tags": ["backend", "observability"]
}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{
    "title": "Add request-id middleware",
    "priority": 2,
    "tags": ["backend", "observability"]
  }'
```


```ts
await client.agent.todos.createTodo(
  {
    title: "Add request-id middleware",
    priority: 2,
    tags: ["backend", "observability"]
  },
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMRCJ5H3Q8Y7N1D9V5X6ZKPA",
  "title": "Add request-id middleware",
  "state": "open",
  "priority": 2,
  "tags": ["backend", "observability"],
  "revision": 1,
  "deduped": false
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |



### `PATCH /api/v1/agent/todos/{id}`

Applies a CAS-guarded field patch or state transition to a todo (`todos.update`). Pass the current revision; a stale revision is rejected.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

CAS-guarded field patch or state transition (forwarded to `todos.update`; the `{id}` comes from the path). Only the keys present are patched.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `revision` | integer | Yes | The TODO's own current `revision` (from `getTodo` — NOT the store-wide revision); a stale value is rejected. |
| `title` | string | No | New title. |
| `body` | string | No | New body. |
| `state` | string | No | New state transition. |
| `priority` | integer | No | New priority. |
| `rank` | integer | No | New ordering rank. |
| `tags` | array | No | New tag set. |
| `cwd` | string | No | Retarget the todo's working directory. |

```json
{
  "revision": 7,
  "state": "in_progress",
  "priority": 1,
  "tags": ["backend", "p1", "observability"]
}
```



```bash
curl -X PATCH "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{
    "revision": 7,
    "state": "in_progress",
    "priority": 1
  }'
```


```ts
await client.agent.todos.updateTodo(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  { revision: 7, state: "in_progress", priority: 1 },
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "state": "in_progress",
  "priority": 1,
  "revision": 8
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "code": "todo_conflict",
  "message": "todo_conflict: revision mismatch (refetch and retry)"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `todo_conflict` | Todo revision conflict | The supplied revision is stale (a concurrent update advanced the todo). Refetch the todo's own revision and retry. | GET the todo (`getTodo`) for its current `revision` — the per-todo CAS token, NOT the store-wide `getTodosRevision` — and re-issue the update. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |



## Archive, claim, release, snooze

### `POST /api/v1/agent/todos/{id}/archive`

Archives a todo (`todos.archive`).

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

CAS guard (forwarded to `todos.archive`; the `{id}` comes from the path).

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `revision` | integer | Yes | The TODO's own current `revision` (from `getTodo`); required — a stale or absent value is rejected. |

```json
{
  "revision": 8
}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/archive" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{"revision": 8}'
```


```ts
await client.agent.todos.archiveTodo(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  { revision: 8 },
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "state": "archived",
  "revision": 9
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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}/claim`

Claims a todo for the caller (`todos.claim`).

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

CAS guard (forwarded to `todos.claim`; the `{id}` comes from the path).

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `revision` | integer | No | The TODO's own current `revision` (from `getTodo`); required for a fresh claim (only the lease's existing owner may refresh without it). |

```json
{
  "revision": 8
}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/claim" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{"revision": 8}'
```


```ts
await client.agent.todos.claimTodo(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  { revision: 8 },
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "state": "claimed",
  "owner": "agent:alpha",
  "lease_until": "2026-04-12T10:15:24Z",
  "revision": 9
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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}/release`

Releases a claimed todo (`todos.release`).

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

JSON object forwarded to the daemon `todos.release` RPC. Reserved `_`-prefixed keys are ignored and the request scope (cwd and config_dir) is applied automatically; all other keys are passed through. The todo `{id}` comes from the path; the body is optional.

```json
{}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/release" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{}'
```


```ts
await client.agent.todos.releaseTodo(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  {},
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "state": "open",
  "owner": null,
  "revision": 10
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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}/snooze`

Snoozes a todo until a wake time (`todos.snooze`).

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

Snooze target (forwarded to `todos.snooze`; the `{id}` comes from the path). The daemon reads `wake_at` + the CAS `revision` — there is no `until` field.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `wake_at` | string | Yes | Wake time, RFC3339 (e.g. `2026-07-04T09:00:00Z`); an empty string clears the snooze. |
| `revision` | integer | Yes | The TODO's own current `revision` (from `getTodo`); a stale value is rejected. |

```json
{
  "wake_at": "2026-07-04T09:00:00Z",
  "revision": 8
}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/snooze" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{
    "wake_at": "2026-07-04T09:00:00Z",
    "revision": 8
  }'
```


```ts
await client.agent.todos.snoozeTodo(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  { wake_at: "2026-07-04T09:00:00Z", revision: 8 },
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "state": "snoozed",
  "wake_at": "2026-07-04T09:00:00Z",
  "revision": 9
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |



## Run and cancel

### `POST /api/v1/agent/todos/{id}/run`

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"). The minted job completes at DISPATCH (it carries the worker `session_id` for correlation, but the resident worker runs asynchronously and is NOT tracked by the job lifecycle): a `succeeded` job means the run was STARTED, not that the TODO is finished. Poll the TODO itself (`getTodo`) — it lands in `review` when the worker is done.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

JSON object forwarded to the daemon `todos.run` RPC. Reserved `_`-prefixed keys are ignored and the request scope (cwd and config_dir) is applied automatically; all other keys are passed through. The todo `{id}` comes from the path; the body is optional.

```json
{}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/run" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{}'
```


```ts
await client.agent.todos.runTodo(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  {},
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "job_id": "j_01HMRD2X8Q4K7T1M5Y8C9V0JNX",
  "session_id": "s_01HMRD2X9B5P6A3F7H2N1W8RKG"
}
```


```json
{
  "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. |


```json
{
  "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 | Forbidden — the request was refused by the server's admin gate. | 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) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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}/cancel-run`

Cancels an in-flight orchestrator run for a todo (`todos.cancel_run`).

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

JSON object forwarded to the daemon `todos.cancel_run` RPC. Reserved `_`-prefixed keys are ignored and the request scope (cwd and config_dir) is applied automatically; all other keys are passed through. The todo `{id}` comes from the path; the body is optional.

```json
{}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/cancel-run" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{}'
```


```ts
await client.agent.todos.cancelTodoRun(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  {},
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "state": "open",
  "cancelled_run_id": "j_01HMRD2X8Q4K7T1M5Y8C9V0JNX"
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |



## Comments and messages

### `POST /api/v1/agent/todos/{id}/messages`

Posts a comment onto a todo's timeline WITHOUT triggering an orchestrator turn (`todos.post_message`). Use `/message` (singular) to also kick an orchestrator turn.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

Comment text (forwarded to `todos.post_message`; the `{id}` comes from the path).

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `text` | string | Yes | The comment body to append to the todo timeline. |

```json
{
  "text": "Spoke with the platform team — they want this shipped next sprint."
}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/messages" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{"text": "Spoke with the platform team — they want this shipped next sprint."}'
```


```ts
await client.agent.todos.postTodoComment(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  { text: "Spoke with the platform team — they want this shipped next sprint." },
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "comment_id": "c_01HMRD5T3P9K8Q2N1V4X6M0YJF",
  "revision": 9
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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}/message`

Posts a comment AND kicks an orchestrator turn on the todo (`todos.message`). Returns `{job_id}`, but the job completes at DISPATCH — the orchestrator runs as a daemon-managed resident whose turn is NOT tracked by the job lifecycle. Poll the todo itself (`getTodo`) for the orchestrator's reply or proposal on the timeline; a `succeeded` job means the turn was dispatched, not finished.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

Comment text that kicks an orchestrator turn (forwarded to `todos.message`; the `{id}` comes from the path). Returns `{job_id}`.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `text` | string | Yes | The message that both comments and prompts the orchestrator. |

```json
{
  "text": "Investigate the auth bug and propose a fix in the next reply."
}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/message" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{"text": "Investigate the auth bug and propose a fix in the next reply."}'
```


```ts
await client.agent.todos.messageTodo(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  { text: "Investigate the auth bug and propose a fix in the next reply." },
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "job_id": "j_01HMRD7K9F2V6Q3H5B8Y1N0MXR"
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |



## Proposals

### `POST /api/v1/agent/todos/{id}/proposals/{pid}/approve`

Approves a run proposal on a todo (`todos.approve_proposal`). Approval is NOT inert: the daemon hands the approved proposal to a background worker session equivalent to `todos.run` — a J-class autonomous run. (The paired `denyTodoProposal` spawns no worker.)

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `pid` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

JSON object forwarded to the daemon `todos.approve_proposal` RPC. Reserved `_`-prefixed keys are ignored and the request scope (cwd and config_dir) is applied automatically; all other keys are passed through. The todo `{id}` and proposal `{pid}` come from the path; the body is optional.

```json
{}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/proposals/p_01HMRD6Q2W8N5Y7K3T1M9F4HXJ/approve" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{}'
```


```ts
await client.agent.todos.approveTodoProposal(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "p_01HMRD6Q2W8N5Y7K3T1M9F4HXJ",
  {},
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "proposal_id": "p_01HMRD6Q2W8N5Y7K3T1M9F4HXJ",
  "state": "approved",
  "run_id": "j_01HMRD7N5P3K9X2Q6V1B8Y4M0F"
}
```


```json
{
  "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. |


```json
{
  "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 | Forbidden — the request was refused by the server's admin gate. | 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) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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`

Denies a run proposal on a todo (`todos.deny_proposal`).

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Path identifier. |
| `pid` | path | string | Yes | Path identifier. |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

JSON object forwarded to the daemon `todos.deny_proposal` RPC. Reserved `_`-prefixed keys are ignored and the request scope (cwd and config_dir) is applied automatically; all other keys are passed through. The todo `{id}` and proposal `{pid}` come from the path; the body is optional.

```json
{}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT/proposals/p_01HMRD6Q2W8N5Y7K3T1M9F4HXJ/deny" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{}'
```


```ts
await client.agent.todos.denyTodoProposal(
  "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "p_01HMRD6Q2W8N5Y7K3T1M9F4HXJ",
  {},
  { "X-Hoody-Cwd": "/home/user/project" }
);
```


```json
{
  "id": "t_01HMR8K5C5Z0V6FQ3NGAZ8X9TT",
  "proposal_id": "p_01HMRD6Q2W8N5Y7K3T1M9F4HXJ",
  "state": "denied"
}
```


```json
{
  "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. |


```json
{
  "code": "forbidden",
  "message": "request must arrive through the Hoody proxy"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `forbidden` | Forbidden (not via the Hoody proxy) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |



## Bulk operations

### `POST /api/v1/agent/todos/purge`

Permanently and irreversibly removes archived todos (`todos.purge`).


This operation permanently deletes archived todos. There is no undo.


### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

JSON object forwarded to the daemon `todos.purge` RPC. Reserved `_`-prefixed keys are ignored and the request scope (cwd and config_dir) is applied automatically; all other keys are passed through. Optional filters scoping which archived todos to purge; an empty body purges all archived tombstones for the cwd.

```json
{}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/purge" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{}'
```


```ts
await client.agent.todos.purgeTodos({}, { "X-Hoody-Cwd": "/home/user/project" });
```


```json
{
  "purged_count": 14
}
```


```json
{
  "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. |


```json
{
  "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 | Forbidden — the request was refused by the server's admin gate. | 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) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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/triage`

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 — the same property that makes `todos.run` a privileged op.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope: 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 selecting which on-disk `.hoody` install a stateless read/write resolves (HoodyPaths). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector: `"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 — 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. |

### Request body

JSON object forwarded to the daemon `todos.triage` RPC. Reserved `_`-prefixed keys are ignored and the request scope (cwd and config_dir) is applied automatically; all other keys are passed through. Optional triage scoping; an empty body triages the whole inbox. Returns `{job_id}`.

```json
{}
```



```bash
curl -X POST "https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.icu/api/v1/agent/todos/triage" \
  -H "Content-Type: application/json" \
  -H "X-Hoody-Cwd: /home/user/project" \
  -d '{}'
```


```ts
await client.agent.todos.triageTodos({}, { "X-Hoody-Cwd": "/home/user/project" });
```


```json
{
  "job_id": "j_01HMR9QX8K7F4M2J5V0YCN1TRB"
}
```


```json
{
  "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. |


```json
{
  "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 | Forbidden — the request was refused by the server's admin gate. | 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) | Forbidden — the request did not reach the service through the public endpoint. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy), not by connecting to the container directly. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |


```json
{
  "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. |