# Agent: Settings

**Page:** api/agent/settings

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

---

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



The Agent: Settings API exposes the runtime configuration that governs how the Hoody daemon resolves, merges, and persists its layered settings (`~/.hoody/settings.json` + project layer + local overrides), manages model-fusion composites (named groups of model members that can be addressed as a single routing target), and configures the BYOA/ACP delegated-session backend (currently `claude`) and its secrets. Use these endpoints to inspect the merged effective settings, apply shallow patches to non-protected top-level keys, curate fusion composites, and store per-agent secret values out-of-band in the 0600 secrets store.


All routes on this page are **process-wide / global / no-realm**. Supplying a per-request realm header (or `?realm=`) returns `400 realm_scope_unsupported`.


## Settings

### `GET /api/v1/agent/settings`

Returns the effective merged settings (home -> project -> `settings.local.json`, top-level merge) plus the home-layer object that the patch endpoint mutates.



```bash
curl -X GET 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/settings' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f'
```


```ts
await client.agent.settings.getSettings({ XHoodyContainer: 'cnt_1a2b3c4d5e6f' })
```



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

#### Responses



```json
{
  "theme": "dark",
  "model": null,
  "autoCompactThreshold": 90,
  "defaultModel": "claude-sonnet-4.5",
  "home": {
    "theme": "dark",
    "autoCompactThreshold": 90
  }
}
```


```json
{
  "code": "realm_scope_unsupported",
  "message": "realm scope not supported on this route"
}
```

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



---

### `PATCH /api/v1/agent/settings`

Applies a **shallow** top-level merge into the home `~/.hoody/settings.json`. A `null` value deletes a key. Top-level only — sending `{"features":{...}}` replaces the whole `features` object. Returns the resulting home object.


Security-load-bearing keys (`deny_list`, `deny_urls`, `features`, `allowed_directories`, `hooks`, `disableAllHooks`, `mcp_servers`, `tools`) cannot be modified over HTTP and return `403 settings_key_protected`. Edit `settings.json` at the host to change a guardrail.

`mcp_servers` is the one guardrail with a dedicated HTTP surface of its own: the `/api/v1/agent/mcp/*` routes write it through a two-step nonce-and-hash flow rather than a generic patch. See [Connect MCP servers](/kit/agent/#connect-mcp-servers).




```bash
curl -X PATCH 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/settings' \
  -H 'Content-Type: application/json' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f' \
  -d '{ "patch": { "theme": "light", "autoCompactThreshold": 95 } }'
```


```ts
await client.agent.settings.patchSettings(
  { patch: { theme: 'light', autoCompactThreshold: 95 } },
  { XHoodyContainer: 'cnt_1a2b3c4d5e6f' }
)
```



#### 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

| Name | Required | Type | Description |
|------|----------|------|-------------|
| `patch` | Yes | object | Top-level keys to merge into the home `settings.json`. A `null` value deletes the key. A non-null top-level `model` is rejected 400 (nothing consumes it). |

#### Responses



```json
{
  "theme": "light",
  "autoCompactThreshold": 95,
  "defaultModel": "claude-sonnet-4.5"
}
```


```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": "settings_key_protected",
  "message": "settings key 'deny_list' is security-load-bearing and cannot be modified over HTTP"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `settings_key_protected` | Settings key protected | The patch targets a security-load-bearing top-level settings key (`deny_list`, `features`, `allowed_directories`, `hooks`, `mcp_servers`, ...). These guardrail keys cannot be modified over HTTP. | Edit `settings.json` at the host to change a guardrail key; omit it from the HTTP patch. For `mcp_servers`, use the dedicated `/api/v1/agent/mcp/*` routes instead. |
| `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": "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. |



---

## Fusion Composites

### `GET /api/v1/agent/settings/fusion`

Lists the persisted model-fusion composites (`fusion.list`). Pass `?include_invalid=true` to also receive entries that failed validation as a top-level `invalid` array beside `items` (each with the failure reason and its stable raw-file index; never mixed into or paginated with the selectable items) so a broken composite is editable/deletable.



```bash
curl -X GET 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/settings/fusion?page=1&limit=20&include_invalid=false' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f'
```


```ts
await client.agent.settings.listFusionIterator({
  page: 1,
  limit: 20,
  include_invalid: false,
  XHoodyContainer: 'cnt_1a2b3c4d5e6f'
})
```



#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `include_invalid` | query | boolean | No | When true, also return composites that failed validation as a top-level `invalid` array beside `items` (each with a reason + raw-file index) so a broken composite is editable/deletable. |
| `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. |

#### Responses



```json
{
  "items": [
    {
      "name": "ensemble-fast",
      "method": "weighted-vote",
      "members": [
        { "model": "claude-haiku-4.5", "weight": 0.6 },
        { "model": "claude-sonnet-4.5", "weight": 0.4 }
      ],
      "context_window": 200000
    }
  ],
  "meta": {
    "total": 3,
    "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": "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. |



---

### `PUT /api/v1/agent/settings/fusion/{slug}`

Creates or updates a model-fusion composite keyed by slug (`fusion.upsert`). The persisted key is derived from `spec.name` (lowercased, separators collapsed to `-`); the path `{slug}` must normalize to the same slug or the request is rejected `400 slug_mismatch` — the path cannot rename what the body describes. Structural invariants (name/method/members, cycle guard, non-zero context window) are validated server-side.



```bash
curl -X PUT 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/settings/fusion/ensemble-fast' \
  -H 'Content-Type: application/json' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f' \
  -d '{
    "spec": {
      "name": "ensemble-fast",
      "method": "weighted-vote",
      "members": [
        { "model": "claude-haiku-4.5", "weight": 0.6 },
        { "model": "claude-sonnet-4.5", "weight": 0.4 }
      ],
      "context_window": 200000
    }
  }'
```


```ts
await client.agent.settings.upsertFusion(
  'ensemble-fast',
  {
    spec: {
      name: 'ensemble-fast',
      method: 'weighted-vote',
      members: [
        { model: 'claude-haiku-4.5', weight: 0.6 },
        { model: 'claude-sonnet-4.5', weight: 0.4 }
      ],
      context_window: 200000
    }
  },
  { XHoodyContainer: 'cnt_1a2b3c4d5e6f' }
)
```



#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `slug` | 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

| Name | Required | Type | Description |
|------|----------|------|-------------|
| `spec` | Yes | object | The `FusionSpec` object (`name`, `method`, `members`, ...). The persisted slug is derived from `spec.name`. |

#### Responses



```json
{
  "name": "ensemble-fast",
  "method": "weighted-vote",
  "members": [
    { "model": "claude-haiku-4.5", "weight": 0.6 },
    { "model": "claude-sonnet-4.5", "weight": 0.4 }
  ],
  "context_window": 200000
}
```


```json
{
  "code": "slug_mismatch",
  "message": "path slug \"foo\" does not match spec.name \"bar\" (slug \"bar\")"
}
```

| 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. |
| `slug_mismatch` | Path slug does not match `spec.name` | The path `{slug}` and the slug derived from body `spec.name` normalize to different values; the daemon persists by the `spec.name`-derived slug, so honoring the request would write a composite the URL does not name. | Use the `spec.name`-derived slug in the path (lowercase, separators collapsed to `-`), or change `spec.name`. |


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



---

### `DELETE /api/v1/agent/settings/fusion/{slug}`

Removes a model-fusion composite by slug (`fusion.delete`).



```bash
curl -X DELETE 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/settings/fusion/ensemble-fast' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f'
```


```ts
await client.agent.settings.deleteFusion('ensemble-fast', { XHoodyContainer: 'cnt_1a2b3c4d5e6f' })
```



#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `slug` | 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. |

#### Responses



```json
{
  "removed": true,
  "slug": "ensemble-fast"
}
```


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



---

## BYOA ACP Backend

### `GET /api/v1/agent/acp/agents`

Reports the BYOA (Bring-Your-Own-Agent) delegated-session backend availability (currently `claude`) — enabled flag, on-`PATH` status, and trust posture (`acp.status`). The result is a fixed status object, not a paginated collection. The report is resolved through the layered settings the delegated session would use, so `X-Hoody-Config-Dir` (override) and `X-Hoody-Cwd` (project layer) are folded.

For REMOTE BYOA, a bound `X-Hoody-Container` switches the report to ON-CONTAINER detection/login probing, and `X-Hoody-Realm` scopes that probe. A realm header without a container returns `400 realm_scope_unsupported` (the local report has no realm dimension).



```bash
curl -X GET 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/acp/agents' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f'
```


```ts
await client.agent.settings.getACPStatus({ XHoodyContainer: 'cnt_1a2b3c4d5e6f' })
```



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

#### Responses



```json
{
  "agent": "claude",
  "enabled": true,
  "on_path": true,
  "version": "1.0.32",
  "trust": "verified",
  "secrets_configured": true
}
```


```json
{
  "code": "realm_scope_unsupported",
  "message": "realm scope not supported on this route"
}
```

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



---

### `PUT /api/v1/agent/acp/agents/{agent}/enabled`

Arms (or disarms) a BYOA ACP backend for delegated sessions (`acp.set_enabled`). Writes `tools.acp_agents.<agent>.enabled` in `settings.json`.


A delegated session — `POST /sessions` with `backend: "acp"` — is **refused by the daemon while its backend is disabled**. On a host configured over HTTP this call is the prerequisite for the whole delegated-session capability.




```bash
curl -X PUT 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/acp/agents/claude/enabled' \
  -H 'Content-Type: application/json' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f' \
  -d '{ "enabled": true }'
```


```ts
await client.agent.settings.setACPEnabled(
  'claude',
  { enabled: true },
  { XHoodyContainer: 'cnt_1a2b3c4d5e6f' }
)
```



#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `agent` | 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

| Name | Required | Type | Description |
|------|----------|------|-------------|
| `enabled` | No | boolean | `true` arms the backend; `false` disarms it. Defaults to `true` when omitted. |

#### Responses



```json
{
  "agent": "claude",
  "enabled": true
}
```


```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": "unknown_agent",
  "message": "unknown ACP agent: evilbot (known: claude)"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. |
| `unknown_agent` | Unknown ACP agent | The named ACP agent is not a known BYOA backend, so there is no launch spec whose enablement could be armed. | Use `"claude"` — the only BYOA backend. |


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



---

### `PUT /api/v1/agent/acp/agents/{agent}/model`

Sets the default model (and reasoning effort) a BYOA ACP backend runs on (`acp.set_agent_model`). These are the values the daemon passes to the backend at connect; a delegated session started without an explicit model inherits them. An empty value clears the pin and returns the backend to its own default.



```bash
curl -X PUT 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/acp/agents/claude/model' \
  -H 'Content-Type: application/json' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f' \
  -d '{ "model": "claude-opus-5", "effort": "high" }'
```


```ts
await client.agent.settings.setACPAgentModel(
  'claude',
  { model: 'claude-opus-5', effort: 'high' },
  { XHoodyContainer: 'cnt_1a2b3c4d5e6f' }
)

// Clear both pins and fall back to the backend's own default:
await client.agent.settings.setACPAgentModel(
  'claude',
  { model: '', effort: '' },
  { XHoodyContainer: 'cnt_1a2b3c4d5e6f' }
)
```



#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `agent` | 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

| Name | Required | Type | Description |
|------|----------|------|-------------|
| `model` | No | string | Backend model id or alias. An empty string clears the pin. |
| `effort` | No | string | Reasoning effort (backend-specific). An empty string clears the pin. |

#### Responses



```json
{
  "agent": "claude",
  "model": "claude-opus-5",
  "effort": "high"
}
```

`model` and `effort` echo the stored pins, and are empty strings when cleared.


```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": "unknown_agent",
  "message": "unknown ACP agent: evilbot (known: claude)"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. |
| `unknown_agent` | Unknown ACP agent | The named ACP agent is not a known BYOA backend, so there is no launch spec that would read the pinned model. | Use `"claude"` — the only BYOA backend. |


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



---

### `PUT /api/v1/agent/acp/agents/{agent}/secrets/{key}`

Stores (or clears) one per-backend env VALUE for a BYOA ACP agent (`acp.set_secret`) in the dedicated `0600 ~/.hoody/acp-secrets.env` store under `acp/<agent>/` (atomic temp+rename, `flock`). `settings.json` holds only the env KEY NAMES; the VALUE lives ONLY in the 0600 store. An empty value DELETES (unsets) the reference.


The value is **never** returned. The reply confirms only `(agent, key, stored|cleared)`. A `404 unknown_agent` is returned for an agent that is not a known BYOA backend (`claude` is currently the only one).




```bash
curl -X PUT 'https://proj_a1b2c3d4e5f6-cnt_1a2b3c4d5e6f-agent-1.us-east-1.containers.hoody.icu/api/v1/agent/acp/agents/claude/secrets/ANTHROPIC_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'X-Hoody-Container: cnt_1a2b3c4d5e6f' \
  -d '{ "value": "sk-ant-EXAMPLE-REPLACE-WITH-REAL-KEY" }'
```


```ts
await client.agent.settings.setACPSecret(
  'claude',
  'ANTHROPIC_API_KEY',
  { value: 'sk-ant-EXAMPLE-REPLACE-WITH-REAL-KEY' },
  { XHoodyContainer: 'cnt_1a2b3c4d5e6f' }
)
```



#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `agent` | path | string | Yes | Path identifier. |
| `key` | 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

| Name | Required | Type | Description |
|------|----------|------|-------------|
| `value` | No | string | The env secret value. Empty string clears (unsets) the reference. Stored only in the 0600 `acp-secrets.env` store. |

#### Responses



```json
{
  "agent": "claude",
  "key": "ANTHROPIC_API_KEY",
  "stored": true,
  "cleared": 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": "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": "unknown_agent",
  "message": "unknown ACP agent: evilbot (known: claude)"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `not_found` | Not found | The requested resource does not exist. | Verify the path and identifier. |
| `unknown_agent` | Unknown ACP agent | The named ACP agent is not a known BYOA backend, so a per-agent secret reference would be dead (no launch spec reads it). | Use `"claude"` — the only BYOA backend. |


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