# Agent: Hooks

**Page:** api/agent/hooks

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

---

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



# Agent: Hooks

Manage agent lifecycle hooks: list, upsert, delete, toggle, test-fire, reload from disk, disable all, and acknowledge the hook-trust prompt. Hooks are session-scoped — every operation on this page requires a live `session_id`. Guarded writes (upsert, delete, toggle, disable-all) additionally require a single-use write nonce minted by `beginHookWrite`.


The HTTP edge has no service-level auth. Authorization is owned by `hoody-proxy` (kit network-position trust). Reach the agent only through the Hoody proxy.


## List hooks

`GET /api/v1/agent/hooks`

Lists the lifecycle hooks for a live session. Returns the live + on-disk hook summaries, drift status, per-scope settings files, and the execution-trust probe. `session_id` may be supplied in the body or as the `?session_id` query alias; the body value wins.

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `session_id` | query | string | No | Live session id (hooks are session-scoped; required by the daemon RPC). Query alias of the body `session_id` (the body value wins). |
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1): the .hoody project layer / record cwd / tool+workflow cwd. |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1) selecting which on-disk .hoody install a stateless read/write resolves. |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). Rejected (400) on routes with no container dimension. |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1): "global" or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header (read only when the header is absent). |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `session_id` | string | No | Live session id (hooks are session-scoped; required by the daemon RPC). May be supplied here OR as the `?session_id` query alias; the body value wins. |



```bash
curl -X GET "https://api.hoody.com/api/v1/agent/hooks?session_id=sess_8f3a2b1c4d5e6f70" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "sess_8f3a2b1c4d5e6f70"}'
```


```typescript
const result = await client.agent.hooks.listHooks({
  session_id: "sess_8f3a2b1c4d5e6f70",
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
});
```


```json
{
  "live": [
    {
      "event": "PreToolUse",
      "matcher": "Bash",
      "command": "echo running pre-tool hook",
      "enabled": true,
      "timeout": 5000
    }
  ],
  "session": {
    "id": "sess_8f3a2b1c4d5e6f70",
    "hooks_root": "/home/user/projects/myapp/.hoody/hooks"
  },
  "stale": false,
  "files": [
    {
      "scope": "project",
      "path": "/home/user/projects/myapp/.hoody/settings.json",
      "mtime": "2025-01-12T09:31:02Z"
    }
  ],
  "trust": {
    "prompted": false,
    "acknowledged": true
  }
}
```


```json
{
  "code": "bad_request",
  "message": "session_id is required"
}
```

| 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) | The request did not arrive through `hoody-proxy`: its source IP is private/local. The gateway has no service-level auth — authorization is owned by `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


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



## Begin a hook write

`POST /api/v1/agent/hooks/begin-write`

Issues the two-step write nonce required before **every** guarded hook mutation: not just upsert/delete, but also toggle and disable-all. The returned nonce binds to a `(session, op, scope)` tuple and is single-use. A nonce minted for `op:toggle` is NOT accepted by `upsert`, etc.


Call this first, then immediately use the returned `nonce` on the matching write. The daemon RPC fails closed without it.


### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1): "global" or a 24-hex id. Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header. |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `session_id` | string | Yes | Live session id (hooks are session-scoped). |
| `op` | string | Yes | The write the nonce authorizes; one of: `upsert`, `delete`, `toggle`, `set_disabled`. The nonce is rejected by any other op. |
| `scope` | string | Yes | Scope of the settings file the write targets (e.g. `project`/`user`); the nonce binds to its resolved path. |



```bash
curl -X POST "https://api.hoody.com/api/v1/agent/hooks/begin-write" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "sess_8f3a2b1c4d5e6f70",
    "op": "upsert",
    "scope": "project"
  }'
```


```typescript
const { nonce } = await client.agent.hooks.beginHookWrite({
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
}, {
  session_id: "sess_8f3a2b1c4d5e6f70",
  op: "upsert",
  scope: "project"
});
```


```json
{
  "nonce": "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
  "op": "upsert",
  "scope": "project",
  "scope_path": "/home/user/projects/myapp/.hoody/settings.json",
  "expires_at": "2025-01-12T09:36:02Z"
}
```


```json
{
  "code": "bad_request",
  "message": "invalid op: must be one of upsert, delete, toggle, set_disabled"
}
```

| 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. | 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) | The request did not arrive through `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


```json
{
  "code": "not_found",
  "message": "session 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`; default 8 MiB). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`) before the handler reads it. | Reduce the request body below the configured limit; 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. |



## Upsert a hook

`PUT /api/v1/agent/hooks`

Creates or updates a hook. Requires a `nonce` minted by `beginHookWrite` for `op:upsert` and a live `session_id`. Defining a hook persists an arbitrary command that fires on lifecycle events.


The HTTP edge has no service-level auth. Securing the public surface (a hook persists an arbitrary command) is `hoody-proxy`'s responsibility.


### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header. |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `session_id` | string | Yes | Live session id (hooks are session-scoped). |
| `nonce` | string | Yes | The single-use write nonce from `beginHookWrite` minted for `op:upsert` + this scope; the RPC fails closed without it. |
| `scope` | string | No | Scope of the settings file to write (must match the nonce's scope). |
| `event` | string | No | Lifecycle event the hook fires on. |
| `matcher` | string | No | Matcher selecting when the hook fires. |
| `command` | string | No | Command to run when the hook fires. |
| `timeout` | integer | No | Per-fire timeout. |



```bash
curl -X PUT "https://api.hoody.com/api/v1/agent/hooks" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "sess_8f3a2b1c4d5e6f70",
    "nonce": "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
    "scope": "project",
    "event": "PreToolUse",
    "matcher": "Bash",
    "command": "echo running pre-tool hook",
    "timeout": 5000
  }'
```


```typescript
const result = await client.agent.hooks.upsertHook({
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
}, {
  session_id: "sess_8f3a2b1c4d5e6f70",
  nonce: "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
  scope: "project",
  event: "PreToolUse",
  matcher: "Bash",
  command: "echo running pre-tool hook",
  timeout: 5000
});
```


```json
{
  "upserted": true,
  "hook": {
    "event": "PreToolUse",
    "matcher": "Bash",
    "command": "echo running pre-tool hook",
    "enabled": true,
    "timeout": 5000,
    "scope": "project"
  }
}
```


```json
{
  "code": "bad_request",
  "message": "invalid nonce for op:upsert"
}
```

| 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. | 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) | The request did not arrive through `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


```json
{
  "code": "not_found",
  "message": "session 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`; default 8 MiB). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`). | Reduce the request body below the configured limit; 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 a hook

`DELETE /api/v1/agent/hooks`

Deletes a hook. Requires the `begin-write` nonce minted by `beginHookWrite` for `op:delete` and a live `session_id`.

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header. |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `session_id` | string | Yes | Live session id (hooks are session-scoped). |
| `nonce` | string | Yes | The single-use write nonce from `beginHookWrite` minted for `op:delete` + this scope; the RPC fails closed without it. |
| `scope` | string | No | Scope of the settings file to write (must match the nonce's scope). |



```bash
curl -X DELETE "https://api.hoody.com/api/v1/agent/hooks" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "sess_8f3a2b1c4d5e6f70",
    "nonce": "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
    "scope": "project"
  }'
```


```typescript
const result = await client.agent.hooks.deleteHook({
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
}, {
  session_id: "sess_8f3a2b1c4d5e6f70",
  nonce: "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
  scope: "project"
});
```


```json
{
  "deleted": true,
  "scope": "project"
}
```


```json
{
  "code": "bad_request",
  "message": "invalid nonce for op:delete"
}
```

| 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. | 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) | The request did not arrive through `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


```json
{
  "code": "not_found",
  "message": "session 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. |



## Toggle a hook

`POST /api/v1/agent/hooks/toggle`

Toggles a single hook's enabled state. Requires the `begin-write` nonce minted by `beginHookWrite` for `op:toggle` and a live `session_id`.

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header. |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `session_id` | string | Yes | Live session id (hooks are session-scoped). |
| `nonce` | string | Yes | The single-use write nonce from `beginHookWrite` minted for `op:toggle` + this scope; the RPC fails closed without it. |
| `scope` | string | No | Scope of the settings file to write (must match the nonce's scope). |



```bash
curl -X POST "https://api.hoody.com/api/v1/agent/hooks/toggle" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "sess_8f3a2b1c4d5e6f70",
    "nonce": "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
    "scope": "project"
  }'
```


```typescript
const result = await client.agent.hooks.toggleHook({
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
}, {
  session_id: "sess_8f3a2b1c4d5e6f70",
  nonce: "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
  scope: "project"
});
```


```json
{
  "toggled": true,
  "scope": "project",
  "enabled": false
}
```


```json
{
  "code": "bad_request",
  "message": "invalid nonce for op:toggle"
}
```

| 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. | 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) | The request did not arrive through `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


```json
{
  "code": "not_found",
  "message": "session 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`; default 8 MiB). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`). | Reduce the request body below the configured limit; 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. |



## Disable all hooks

`POST /api/v1/agent/hooks/disable-all`

Disables (or re-enables) all hooks at once. Requires the `begin-write` nonce minted by `beginHookWrite` for `op:set_disabled` and a live `session_id`. The daemon RPC fails closed without the nonce.

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header. |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `session_id` | string | Yes | Live session id (hooks are session-scoped). |
| `nonce` | string | Yes | The single-use write nonce from `beginHookWrite` minted for `op:set_disabled` + this scope; the RPC fails closed without it. |
| `scope` | string | No | Scope of the settings file to write (must match the nonce's scope). |



```bash
curl -X POST "https://api.hoody.com/api/v1/agent/hooks/disable-all" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "sess_8f3a2b1c4d5e6f70",
    "nonce": "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
    "scope": "project"
  }'
```


```typescript
const result = await client.agent.hooks.disableAllHooks({
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
}, {
  session_id: "sess_8f3a2b1c4d5e6f70",
  nonce: "nonce_4d2c1e8f9a0b6c5d7e3f1a2b8c9d0e1f",
  scope: "project"
});
```


```json
{
  "disabled": true,
  "scope": "project"
}
```


```json
{
  "code": "bad_request",
  "message": "invalid nonce for op:set_disabled"
}
```

| 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. | 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) | The request did not arrive through `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


```json
{
  "code": "not_found",
  "message": "session 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`; default 8 MiB). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`). | Reduce the request body below the configured limit; 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. |



## Reload hooks from disk

`POST /api/v1/agent/hooks/reload`

Reloads the hook configuration from disk. Pass a live `session_id` to also receive the reloaded summary.

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header. |

### Request Body

This endpoint takes no body fields.



```bash
curl -X POST "https://api.hoody.com/api/v1/agent/hooks/reload" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{}'
```


```typescript
const result = await client.agent.hooks.reloadHooks({
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
});
```


```json
{
  "reloaded": true,
  "files": [
    {
      "scope": "project",
      "path": "/home/user/projects/myapp/.hoody/settings.json"
    }
  ]
}
```


```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. | 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) | The request did not arrive through `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


```json
{
  "code": "not_found",
  "message": "session 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`; default 8 MiB). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`). | Reduce the request body below the configured limit; 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. |



## Test-fire a hook

`POST /api/v1/agent/hooks/test`

Test-fires a hook command — this **executes an arbitrary command now**. The daemon's human-only `_machine_confirmed` denial lives only on the model-facing tool path, NOT on the `hooks.test` RPC this route dispatches. The HTTP edge has no service-level auth; securing the public surface is `hoody-proxy`'s responsibility. Requires a live `session_id` (hooks are session-scoped).


This endpoint executes an arbitrary command. Treat it as RCE-level access. Make sure all traffic reaches the agent only through `hoody-proxy`.


### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header. |

### Request Body

This endpoint takes no body fields.



```bash
curl -X POST "https://api.hoody.com/api/v1/agent/hooks/test" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{}'
```


```typescript
const result = await client.agent.hooks.testHook({
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
});
```


```json
{
  "fired": true,
  "exit_code": 0,
  "stdout": "running pre-tool hook\n",
  "stderr": ""
}
```


```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. | 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) | The request did not arrive through `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


```json
{
  "code": "not_found",
  "message": "session 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`; default 8 MiB). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`). | Reduce the request body below the configured limit; 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. |



## Acknowledge hook trust

`POST /api/v1/agent/hooks/trust/ack`

Acknowledges the hook-trust prompt. Requires a live `session_id` (hooks are session-scoped).

### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `X-Hoody-Cwd` | header | string | No | Per-request working-directory scope (§6.1). |
| `X-Hoody-Config-Dir` | header | string | No | Per-request `--config-dir` override (§6.1). |
| `X-Hoody-Container` | header | string | No | Per-request bound remote container (§6.1; omitted = local). |
| `X-Hoody-Realm` | header | string | No | Per-request realm selector (§6.1). Rejected (400 `realm_scope_unsupported`) on active-only / no-realm routes. |
| `realm` | query | string | No | Per-request realm selector (§6.1) — the in:query alias of the `X-Hoody-Realm` header. |

### Request Body

This endpoint takes no body fields.



```bash
curl -X POST "https://api.hoody.com/api/v1/agent/hooks/trust/ack" \
  -H "X-Hoody-Cwd: /home/user/projects/myapp" \
  -H "X-Hoody-Config-Dir: /home/user/.hoody" \
  -H "X-Hoody-Container: local" \
  -H "Content-Type: application/json" \
  -d '{}'
```


```typescript
const result = await client.agent.hooks.ackHookTrust({
  "X-Hoody-Cwd": "/home/user/projects/myapp",
  "X-Hoody-Config-Dir": "/home/user/.hoody",
  "X-Hoody-Container": "local"
});
```


```json
{
  "acknowledged": true,
  "session_id": "sess_8f3a2b1c4d5e6f70"
}
```


```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. | 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) | The request did not arrive through `hoody-proxy`. | Reach the agent through `hoody-proxy` (e.g. `hoody agent …` → platform → proxy). |


```json
{
  "code": "not_found",
  "message": "session 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`; default 8 MiB). The gateway rejects an oversized body at the edge (`http.MaxBytesReader`). | Reduce the request body below the configured limit; 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. |