> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rkat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# REST API

> HTTP server for running and managing agent sessions over REST endpoints.

Meerkat ships a REST server for running and managing agent sessions over HTTP. This is the best fit if you want a simple, language-agnostic API for the Meerkat core.

## Getting started

<Steps>
  <Step title="Start the server">
    <CodeGroup>
      ```bash Installed binary theme={null}
      rkat-rest --realm team-alpha
      ```
    </CodeGroup>
  </Step>

  <Step title="Set API keys">
    Export the key for your chosen provider:

    ```bash theme={null}
    export ANTHROPIC_API_KEY="sk-..."
    # or OPENAI_API_KEY, GOOGLE_API_KEY
    ```
  </Step>

  <Step title="Send a request">
    ```bash theme={null}
    curl -X POST http://127.0.0.1:8080/sessions \
      -H "Content-Type: application/json" \
      -d '{"prompt": "Hello, Meerkat!"}'
    ```
  </Step>
</Steps>

## Runtime scope

`rkat-rest` accepts:

* `--realm <id>`
* `--isolated`
* `--instance <id>`
* `--realm-backend <sqlite|jsonl>`
* `--state-root <path>`
* `--context-root <path>`
* `--user-config-root <path>`
* `--expose-paths`

If `--realm` is omitted, the server creates a new isolated opaque realm (`realm-...`).
`--realm-backend` is a creation hint only; after first open, `realm_manifest.json`
is authoritative.

## Endpoint overview

This overview follows the generated OpenAPI artifact at
`artifacts/schemas/rest-openapi.json`.

| Method   | Path                                         | Description                                                 |
| -------- | -------------------------------------------- | ----------------------------------------------------------- |
| `POST`   | `/help`                                      | Ask Meerkat usage help with the embedded platform skill     |
| `POST`   | `/sessions`                                  | Create and run a new session                                |
| `GET`    | `/sessions`                                  | List sessions                                               |
| `GET`    | `/sessions/{id}`                             | Fetch session metadata and usage                            |
| `GET`    | `/sessions/{id}/status`                      | Fetch current runtime state for a session                   |
| `GET`    | `/sessions/{id}/history`                     | Fetch committed session transcript messages                 |
| `POST`   | `/sessions/{id}/system_context`              | Append staged runtime system context                        |
| `POST`   | `/sessions/{id}/messages`                    | Continue an existing session                                |
| `POST`   | `/sessions/{id}/external-events`             | Queue a runtime-backed external event                       |
| `POST`   | `/sessions/{id}/peer-response-terminal`      | Queue a typed peer terminal response event                  |
| `POST`   | `/sessions/{id}/interrupt`                   | Interrupt an in-flight turn                                 |
| `DELETE` | `/sessions/{id}`                             | Archive (remove) a session                                  |
| `GET`    | `/sessions/{id}/events`                      | SSE stream for real-time updates                            |
| `GET`    | `/schedule/tools`                            | List schedule tool definitions                              |
| `POST`   | `/schedule/call`                             | Call a schedule tool directly                               |
| `POST`   | `/schedules`                                 | Create a schedule                                           |
| `GET`    | `/schedules`                                 | List schedules                                              |
| `GET`    | `/schedules/{id}`                            | Fetch one schedule                                          |
| `PATCH`  | `/schedules/{id}`                            | Update a schedule                                           |
| `DELETE` | `/schedules/{id}`                            | Delete a schedule                                           |
| `POST`   | `/schedules/{id}/pause`                      | Pause a schedule                                            |
| `POST`   | `/schedules/{id}/resume`                     | Resume a schedule                                           |
| `GET`    | `/schedules/{id}/occurrences`                | List schedule occurrences                                   |
| `GET`    | `/workgraph/items`                           | List WorkGraph items                                        |
| `GET`    | `/workgraph/items/{id}`                      | Fetch one WorkGraph item                                    |
| `GET`    | `/workgraph/ready`                           | List ready WorkGraph items                                  |
| `GET`    | `/workgraph/snapshot`                        | Read a WorkGraph observability snapshot                     |
| `GET`    | `/workgraph/events`                          | Read WorkGraph event history                                |
| `POST`   | `/sessions/{id}/mcp/add`                     | Stage live MCP server addition (mcp feature)                |
| `POST`   | `/sessions/{id}/mcp/remove`                  | Stage live MCP server removal (mcp feature)                 |
| `POST`   | `/sessions/{id}/mcp/reload`                  | Reload MCP server(s) (mcp feature)                          |
| `POST`   | `/comms/send`                                | Push comms message into a session (comms feature)           |
| `GET`    | `/comms/peers`                               | List discoverable peers (comms feature)                     |
| `GET`    | `/auth/profiles`                             | List realm auth profiles, backend profiles, and bindings    |
| `POST`   | `/auth/profiles`                             | Store binding-scoped credentials                            |
| `GET`    | `/auth/bindings/{binding_id}`                | Read a binding-scoped auth profile                          |
| `DELETE` | `/auth/bindings/{binding_id}`                | Delete binding-scoped credentials                           |
| `POST`   | `/auth/bindings/{binding_id}/test`           | Test a binding resolve path                                 |
| `POST`   | `/auth/login/start`                          | Start interactive auth login                                |
| `POST`   | `/auth/login/complete`                       | Complete interactive auth login                             |
| `POST`   | `/auth/login/device/start`                   | Start device-code login                                     |
| `POST`   | `/auth/login/device/complete`                | Complete device-code login                                  |
| `GET`    | `/auth/bindings/{binding_id}/status`         | Read binding auth status                                    |
| `POST`   | `/auth/bindings/{binding_id}/logout`         | Log out a binding                                           |
| `GET`    | `/realms`                                    | List configured realms                                      |
| `GET`    | `/realms/{id}`                               | Read one realm                                              |
| `GET`    | `/mob/{id}/events`                           | Mob event SSE stream (mob feature)                          |
| `POST`   | `/mob/{id}/spawn-helper`                     | Spawn a helper into a mob (mob feature)                     |
| `POST`   | `/mob/{id}/fork-helper`                      | Fork a helper from an existing member (mob feature)         |
| `POST`   | `/mob/{id}/wait-kickoff`                     | Wait for kickoff completion (mob feature)                   |
| `GET`    | `/mob/{id}/members/{agent_identity}/status`  | Read member status (mob feature)                            |
| `POST`   | `/mob/{id}/members/{agent_identity}/cancel`  | Force-cancel a member (mob feature)                         |
| `POST`   | `/mob/{id}/members/{agent_identity}/respawn` | Respawn a member (mob feature)                              |
| `GET`    | `/skills`                                    | List skills with provenance                                 |
| `GET`    | `/health`                                    | Liveness check                                              |
| `GET`    | `/runtime/host_info`                         | Read runtime host identity, endpoints, and realm projection |
| `GET`    | `/runtime/capabilities`                      | Read runtime host capability flags                          |
| `GET`    | `/runtime/health`                            | Read runtime host health                                    |
| `GET`    | `/models/catalog`                            | Curated model catalog with provider profiles                |
| `GET`    | `/capabilities`                              | Runtime capabilities                                        |
| `GET`    | `/config`                                    | Read config                                                 |
| `PUT`    | `/config`                                    | Replace config                                              |
| `PATCH`  | `/config`                                    | Merge-patch config (RFC 7396)                               |
| `POST`   | `/requests/{request_id}/cancel`              | Cancel an uncommitted in-flight request                     |

<Note>
  Generated images are not returned as inline bytes in history. `GET /sessions/{id}/history` returns assistant image blocks with `image_id`, `blob_ref`, dimensions, and metadata; fetch image bytes through the blob/artifact surface exposed by the runtime-backed RPC/SDK path.
</Note>

<Note>
  REST keeps observation and helper endpoints for mobs, but typed lifecycle/control
  for app hosts lives on the canonical RPC/SDK `mob/*` surface. Inside running
  sessions, mob capability is still exposed by composing `meerkat-mob-mcp`
  (`MobMcpState` + `AgentMobToolSurfaceFactory`) into
  `SessionBuildOptions.mob_tools` in the host runtime. `external_tools` remains
  reserved for callback and MCP-backed dispatchers.
</Note>

<Note>
  WorkGraph REST endpoints are read-only observability/operator lookup. Agents
  create, claim, update, link, evidence, and close WorkGraph items through
  WorkGraph tools inside sessions.
</Note>

## Request cancellation

REST request cancellation is opt-in and request-ID based.

* send `X-Meerkat-Request-Id: <id>` on `POST /sessions` or `POST /sessions/{id}/messages`
* call `POST /requests/{request_id}/cancel` to cancel uncommitted in-flight work
* duplicate in-flight request IDs are rejected

Cancellation only affects uncommitted work:

* pre-start / pre-commit work may return cancelled
* committed success is not rewritten to cancellation
* post-commit create failures still return session identity and remain resumable

## Configuration

<Accordion title="Server configuration details">
  REST configuration is realm-scoped:

  * macOS: `~/Library/Application Support/meerkat/realms/<realm>/config.toml`
  * Linux: `~/.local/share/meerkat/realms/<realm>/config.toml`
  * Windows: `%APPDATA%\\meerkat\\realms\\<realm>\\config.toml`

  Each realm also has `realm_manifest.json` (backend pinning) and `config_state.json` (generation CAS state).

  Key sections:

  ```toml theme={null}
  [rest]
  host = "127.0.0.1"
  port = 8080

  [agent]
  model = "claude-opus-4-6"
  max_tokens_per_turn = 8192

  [tools]
  builtins_enabled = false
  shell_enabled = false
  schedule_enabled = true
  workgraph_enabled = false
  ```

  API keys are provided via environment variables:

  * `ANTHROPIC_API_KEY`
  * `OPENAI_API_KEY`
  * `GOOGLE_API_KEY`
</Accordion>

## Endpoints

### POST /help

Ask Meerkat usage help with the embedded platform skill.

```json Request theme={null}
{
  "question": "How do I add an MCP server?"
}
```

Returns a `HelpResponse` with the answer text and any plan metadata requested by the input.

### POST /sessions

Create and run a new session.

<CodeGroup>
  ```json Request (minimal) theme={null}
  {
    "prompt": "Your prompt here"
  }
  ```

  ```json Request (full) theme={null}
  {
    "prompt": "Your prompt here",
    "system_prompt": "Optional system prompt",
    "model": "claude-opus-4-6",
    "provider": "anthropic",
    "max_tokens": 4096,
    "output_schema": {
      "schema": {"type": "object", "properties": {"answer": {"type": "string"}}},
      "name": "answer",
      "strict": false,
      "compat": "lossy",
      "format": "meerkat_v1"
    },
    "structured_output_retries": 2,
    "verbose": false,
    "keep_alive": null,
    "comms_name": null,
    "peer_meta": null,
    "hooks_override": null,
    "enable_builtins": null,
    "enable_shell": null,
    "enable_memory": null,
    "enable_schedule": null,
    "enable_workgraph": null
  }
  ```

  ```json Response theme={null}
  {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "text": "Response text",
    "turns": 1,
    "tool_calls": 0,
    "usage": {
      "input_tokens": 50,
      "output_tokens": 200,
      "total_tokens": 250
    },
    "structured_output": null,
    "schema_warnings": null
  }
  ```
</CodeGroup>

#### Request fields

<ParamField body="prompt" type="string" required>
  The user prompt to send to the agent.
</ParamField>

<ParamField body="system_prompt" type="string | null" default="null">
  Override the system prompt for this session.
</ParamField>

<ParamField body="model" type="string | null" default="config default">
  Model name (e.g. `"claude-opus-4-6"`, `"gpt-5.5"`).
</ParamField>

<ParamField body="provider" type="string | null" default="inferred from model">
  Provider: `"anthropic"`, `"openai"`, `"gemini"`, `"self_hosted"`, `"other"`.
</ParamField>

<ParamField body="max_tokens" type="u32 | null" default="config default">
  Max tokens per turn.
</ParamField>

<ParamField body="output_schema" type="OutputSchema | null" default="null">
  JSON schema for structured output extraction (wrapper or raw schema).
</ParamField>

<ParamField body="structured_output_retries" type="u32 | null" default="null">
  Max retries for structured output validation. `null`/omitted uses the server default on create and inherits the persisted session value on continue.
</ParamField>

<ParamField body="verbose" type="bool" default="false">
  Enable verbose event logging (server-side).
</ParamField>

<ParamField body="keep_alive" type="bool | null" default="null">
  Keep session alive after turn for comms. On create, `null`/omitted uses the create default (`false`), `true` enables, and `false` explicitly disables. Requires `comms_name` when enabled.
</ParamField>

<ParamField body="comms_name" type="string | null" default="null">
  Agent name for inter-agent communication.
</ParamField>

<ParamField body="peer_meta" type="object | null" default="null">
  Friendly metadata for peer discovery (name, description, labels).
</ParamField>

### GET /sessions/{id}/history

Read committed transcript history for a session without changing the lightweight metadata shape of `GET /sessions/{id}`.

Query parameters:

* `offset` — skip this many messages from the start of the transcript
* `limit` — cap the number of returned messages

```json theme={null}
{
  "session_id": "01936f8a-7b2c-7000-8000-000000000001",
  "message_count": 8,
  "offset": 0,
  "limit": 50,
  "has_more": false,
  "messages": [
    { "role": "system", "content": "You are a helpful assistant." },
    { "role": "user", "content": "Hello" },
    { "role": "assistant", "content": "Hi there." }
  ]
}
```

History is returned oldest-to-newest and reflects the last committed session snapshot only.

<ParamField body="hooks_override" type="HookRunOverrides | null" default="null">
  Run-scoped hook overrides (see [Hooks](/guides/hooks)).
</ParamField>

<ParamField body="enable_builtins" type="bool | null" default="null (factory default)">
  Enable built-in tools (task management, etc.). Omit to use factory defaults.
</ParamField>

<ParamField body="enable_shell" type="bool | null" default="null (factory default)">
  Enable shell tool (requires `enable_builtins`). Omit to use factory defaults.
</ParamField>

<ParamField body="enable_memory" type="bool | null" default="null (factory default)">
  Enable semantic memory. Omit to use factory defaults.
</ParamField>

<ParamField body="enable_schedule" type="bool | null" default="null (factory default)">
  Override schedule tools for this session. Omit to use factory defaults.
</ParamField>

<ParamField body="enable_workgraph" type="bool | null" default="null (factory default)">
  Override WorkGraph tools for this session. Omit to use factory defaults.
</ParamField>

#### Response fields

<ResponseField name="session_id" type="string">
  UUID of the created session.
</ResponseField>

<ResponseField name="text" type="string">
  The agent's response text.
</ResponseField>

<ResponseField name="turns" type="u32">
  Number of LLM calls made.
</ResponseField>

<ResponseField name="tool_calls" type="u32">
  Number of tool calls executed.
</ResponseField>

<ResponseField name="usage" type="WireUsage">
  Token usage breakdown.
</ResponseField>

<ResponseField name="usage.input_tokens" type="u64">
  Input tokens consumed.
</ResponseField>

<ResponseField name="usage.output_tokens" type="u64">
  Output tokens generated.
</ResponseField>

<ResponseField name="usage.total_tokens" type="u64">
  Total tokens (input + output).
</ResponseField>

<ResponseField name="usage.cache_creation_tokens" type="u64 | null">
  Cache creation tokens (provider-specific).
</ResponseField>

<ResponseField name="usage.cache_read_tokens" type="u64 | null">
  Cache read tokens (provider-specific).
</ResponseField>

<ResponseField name="structured_output" type="object | null">
  Parsed structured output (when `output_schema` was provided).
</ResponseField>

<ResponseField name="schema_warnings" type="array | null">
  Schema compatibility warnings per provider.
</ResponseField>

<Accordion title="Structured output example">
  <CodeGroup>
    ```json Request theme={null}
    {
      "prompt": "Extract the capital of France",
      "model": "claude-opus-4-6",
      "output_schema": {
        "schema": {
          "type": "object",
          "properties": {
            "country": {"type": "string"},
            "capital": {"type": "string"}
          },
          "required": ["country", "capital"]
        },
        "name": "capital_extraction",
        "strict": false,
        "compat": "lossy",
        "format": "meerkat_v1"
      },
      "structured_output_retries": 2
    }
    ```

    ```json Response theme={null}
    {
      "session_id": "01936f8a-7b2c-7000-8000-000000000002",
      "text": "The capital of France is Paris.",
      "turns": 1,
      "tool_calls": 0,
      "usage": {
        "input_tokens": 80,
        "output_tokens": 30,
        "total_tokens": 110
      },
      "structured_output": {
        "country": "France",
        "capital": "Paris"
      },
      "schema_warnings": [
        {
          "provider": "gemini",
          "path": "/properties/choice/oneOf",
          "message": "Removed unsupported keyword 'oneOf'"
        }
      ]
    }
    ```
  </CodeGroup>

  When `output_schema` is provided, the `text` field remains the committed
  main-turn assistant output. The extraction turn runs afterward and populates
  `structured_output` on success or `extraction_error` on failure.

  `output_schema` may be provided as a wrapper object (shown above) or as a raw
  JSON Schema object. The wrapper enables explicit `compat`/`format` settings.
</Accordion>

### GET /sessions

List sessions. Supports optional label filters via query parameters.

```json Response theme={null}
{
  "sessions": [
    {
      "session_id": "01936f8a-7b2c-7000-8000-000000000001",
      "state": "idle",
      "created_at": "2025-01-15T10:30:00Z"
    }
  ]
}
```

### POST /sessions/\{id}/messages

Continue an existing session.

<CodeGroup>
  ```json Request theme={null}
  {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "prompt": "Follow-up message"
  }
  ```

  ```json Response theme={null}
  {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "text": "Response to follow-up",
    "turns": 1,
    "tool_calls": 0,
    "usage": {
      "input_tokens": 100,
      "output_tokens": 150,
      "total_tokens": 250
    },
    "structured_output": null,
    "schema_warnings": null
  }
  ```
</CodeGroup>

#### Request fields

<ParamField body="session_id" type="string" required>
  Session ID (must match the path `{id}`).
</ParamField>

<ParamField body="prompt" type="string" required>
  Follow-up prompt.
</ParamField>

<ParamField body="system_prompt" type="string | null" default="null">
  Override the system prompt.
</ParamField>

<ParamField body="model" type="string | null" default="from session">
  Model override for this turn. On materialized sessions this hot-swaps the LLM client for the remainder of the session.
</ParamField>

<ParamField body="provider" type="string | null" default="from session">
  Provider override for this turn. Typically inferred from `model`. Used with `model` for mid-session provider switching.
</ParamField>

<ParamField body="max_tokens" type="u32 | null" default="from session">
  Max tokens override for this turn.
</ParamField>

<ParamField body="output_schema" type="OutputSchema | null" default="null">
  Structured output schema for this turn.
</ParamField>

<ParamField body="structured_output_retries" type="u32 | null" default="null">
  Max retries for structured output validation. Omit / `null` to inherit the persisted session value on continue.
</ParamField>

<ParamField body="verbose" type="bool" default="false">
  Enable verbose event logging.
</ParamField>

<ParamField body="keep_alive" type="bool | null" default="null">
  Keep-alive override for this turn. `null` = inherit persisted session intent, `true` = enable, `false` = disable.
</ParamField>

<ParamField body="comms_name" type="string | null" default="from session">
  Agent name for comms.
</ParamField>

<ParamField body="hooks_override" type="HookRunOverrides | null" default="null">
  Run-scoped hook overrides.
</ParamField>

#### Response fields

Same shape as `POST /sessions`.

### POST /sessions/\{id}/interrupt

Interrupt an in-flight turn. No-op if the session is idle.

```json Response theme={null}
{"interrupted": true}
```

### POST /requests/\{request\_id}/cancel

Cancel an uncommitted in-flight request that previously supplied `X-Meerkat-Request-Id`.

```json Response theme={null}
{"cancelled": true}
```

Returns `200` with `{"cancelled": false, "reason": "already_terminal"}` when
the tracked request already published or completed. Returns `404` only when the
request ID is unknown.

### DELETE /sessions/\{id}

Archive (remove) a session.

```json Response theme={null}
{"archived": true}
```

Returns `404` if the session is not found.

### POST /sessions/\{id}/external-events

Queue an external event through the runtime-backed admission path.

<CodeGroup>
  ```bash Without auth (localhost only) theme={null}
  curl -X POST http://localhost:8080/sessions/sid_abc/external-events \
    -H "Content-Type: application/json" \
    -d '{"alert": "CPU spike", "host": "web-03"}'
  ```

  ```bash With webhook secret theme={null}
  curl -X POST http://localhost:8080/sessions/sid_abc/external-events \
    -H "Content-Type: application/json" \
    -H "X-Webhook-Secret: my-secret" \
    -d '{"alert": "deployment failed"}'
  ```

  ```json Response (202 Accepted) theme={null}
  {"queued": true}
  ```
</CodeGroup>

<Note>
  This route keeps the optional `RKAT_WEBHOOK_SECRET` header auth used for webhook
  delivery, but the admitted event now flows through runtime input acceptance
  instead of a surface-local injector path.
</Note>

<ParamField body="(body)" type="any JSON" required>
  Any JSON payload. Pretty-printed and injected as an event into the agent's inbox.
</ParamField>

<ParamField header="X-Webhook-Secret" type="string">
  Webhook secret for authentication. Required when `RKAT_WEBHOOK_SECRET` env var is set on the server. Compared using constant-time equality (`subtle::ConstantTimeEq`).
</ParamField>

| HTTP Status | When                              |
| ----------- | --------------------------------- |
| 202         | Event queued successfully         |
| 404         | Session not found                 |
| 401         | Missing or invalid webhook secret |
| 503         | Event inbox is full               |

### POST /sessions/\{id}/peer-response-terminal

Admit a terminal peer response through the typed runtime ingress.

```bash theme={null}
curl -X POST http://localhost:8080/sessions/sid_abc/peer-response-terminal \
  -H "Content-Type: application/json" \
  -d '{
    "peer_id": "00000000-0000-4000-8000-000000000161",
    "display_name": "analyst",
    "request_id": "00000000-0000-4000-8000-000000000162",
    "status": "completed",
    "result": {"token": "silver harbor"}
  }'
```

<ParamField body="peer_id" type="string" required>
  Canonical peer routing ID. Display names are not accepted as routing identity.
</ParamField>

<ParamField body="display_name" type="string">
  Optional presentation label.
</ParamField>

<ParamField body="request_id" type="string" required>
  Peer correlation ID for the request this terminal response completes.
</ParamField>

<ParamField body="status" type="string" required>
  Terminal response status: `"completed"`, `"failed"`, or `"cancelled"`.
</ParamField>

<ParamField body="result" type="any JSON" required>
  Peer-returned terminal payload.
</ParamField>

### GET /sessions/\{id}

Fetch session metadata and usage.

```json Response theme={null}
{
  "session_id": "01936f8a-7b2c-7000-8000-000000000001",
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-15T10:31:00Z",
  "message_count": 4,
  "total_tokens": 500
}
```

### GET /sessions/\{id}/events

Server-Sent Events (SSE) stream for real-time updates.

Event types:

| Event                      | Description                               |
| -------------------------- | ----------------------------------------- |
| `session_loaded`           | Emitted on connect with session metadata  |
| `run_started`              | Agent execution began                     |
| `run_completed`            | Agent run finished                        |
| `run_failed`               | Agent run failed                          |
| `turn_started`             | New LLM call within the turn              |
| `text_delta`               | Streaming text chunk from LLM             |
| `text_complete`            | Full text for this turn                   |
| `tool_call_requested`      | LLM wants to call a tool                  |
| `tool_result_received`     | Tool result processed                     |
| `turn_completed`           | LLM call finished                         |
| `tool_execution_started`   | Tool dispatch began                       |
| `tool_execution_completed` | Tool returned a result                    |
| `tool_execution_timed_out` | Tool exceeded timeout                     |
| `compaction_started`       | Context compaction began                  |
| `compaction_completed`     | Compaction finished                       |
| `compaction_failed`        | Compaction failed                         |
| `budget_warning`           | Approaching resource limits               |
| `retrying`                 | Retrying after transient error            |
| `hook_started`             | Hook execution began                      |
| `hook_completed`           | Hook finished                             |
| `hook_failed`              | Hook execution failed                     |
| `hook_denied`              | Hook blocked operation                    |
| `skills_resolved`          | Skills loaded for turn                    |
| `skill_resolution_failed`  | Skill resolution failed                   |
| `done`                     | Emitted when the broadcast channel closes |

### GET /skills

List all skills with provenance information. Returns active and shadowed entries.

```json Response theme={null}
{
  "skills": [
    {
      "key": {
        "source_uuid": "00000000-0000-4b11-8111-000000000001",
        "skill_name": "task-workflow"
      },
      "name": "Task Workflow",
      "description": "How to use task tools",
      "scope": "builtin",
      "source": {
        "source_uuid": "00000000-0000-4b11-8111-000000000001",
        "display_name": "embedded",
        "transport_kind": "embedded",
        "fingerprint": "embedded:inventory",
        "status": "active"
      },
      "is_active": true
    },
    {
      "key": {
        "source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
        "skill_name": "task-workflow"
      },
      "name": "Custom Task Workflow",
      "description": "Override tasks",
      "scope": "project",
      "source": {
        "source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
        "display_name": "company",
        "transport_kind": "git",
        "fingerprint": "repo-7cc66f36fd9db1a1",
        "status": "active"
      },
      "is_active": true
    }
  ]
}
```

Returns `404` if skills are not enabled.

### GET /health

Returns `"ok"` (HTTP 200). Use for liveness checks.

### GET /models/catalog

Return the curated model catalog with provider profiles, capability metadata, and parameter schemas.

```json Response (abbreviated) theme={null}
{
  "contract_version": "0.6.6",
  "providers": [
    {
      "provider": "anthropic",
      "default_model_id": "claude-opus-4-7",
      "models": [
        {
          "id": "claude-opus-4-7",
          "display_name": "Claude Opus 4.7",
          "tier": "recommended",
          "context_window": 1000000,
          "max_output_tokens": 128000,
          "profile": {
            "model_family": "claude-opus-4",
            "supports_temperature": false,
            "supports_thinking": true,
            "supports_reasoning": false,
            "params_schema": {}
          }
        }
      ]
    }
  ]
}
```

The catalog is resolved from built-in model metadata plus config-backed provider/server entries. Each provider entry includes the default model and a list of models with their capabilities and parameter schemas.

### GET /capabilities

Returns runtime capabilities with status resolved against config.

```json Response (abbreviated) theme={null}
{
  "contract_version": {"major": 0, "minor": 6, "patch": 6},
  "capabilities": [
    {
      "id": "sessions",
      "description": "Session lifecycle management",
      "status": "Available"
    },
    {
      "id": "shell",
      "description": "Shell command execution",
      "status": {"DisabledByPolicy": {"description": "Disabled by config"}}
    }
  ]
}
```

The full response includes all capabilities: `sessions`, `streaming`, `structured_output`, `hooks`, `builtins`, `shell`, `comms`, `memory_store`, `schedule`, `work_graph`, `session_store`, `session_compaction`, `skills`, `mcp_live`. See the [RPC capabilities endpoint](/api/rpc#capabilitiesget) for the complete example.

### GET /config

Returns a realm config envelope:

* `config`
* `generation`
* `realm_id`
* `instance_id`
* `backend`
* `resolved_paths`

### PUT /config

Replaces config.

Accepted request forms:

* Direct config object (compat)
* Wrapped form with CAS:
  * `{ "config": <Config>, "expected_generation": <u64|null> }`

### PATCH /config

Applies RFC 7396 merge patch.

Accepted request forms:

* Direct patch object (compat)
* Wrapped form with CAS:
  * `{ "patch": <JSON>, "expected_generation": <u64|null> }`

If `expected_generation` is stale, the server returns `400` with a generation-conflict message.

### GET /auth/profiles

List the auth profiles, backend profiles, and provider bindings for a realm.

<ParamField query="realm_id" type="string" required>
  Realm to read.
</ParamField>

<ParamField query="profile_id" type="string | null">
  Optional profile selector accepted by the shared auth query shape.
</ParamField>

```json Response theme={null}
{
  "realm_id": "prod",
  "auth_profiles": [],
  "backend_profiles": [],
  "bindings": []
}
```

### POST /auth/profiles

Store credentials for an existing binding-scoped auth profile. The binding must
resolve to an auth profile whose source is `managed_store`; inline, env,
external resolver, platform-default, command, and file-descriptor sources are
configured outside this endpoint.

```json Request theme={null}
{
  "realm_id": "prod",
  "binding_id": "openai",
  "profile_id": "prod_openai",
  "provider": "openai",
  "auth_method": "api_key",
  "secret": "sk-..."
}
```

```json Response (201 Created) theme={null}
{
  "realm_id": "prod",
  "binding_id": "openai",
  "auth_binding": {
    "realm": "prod",
    "binding": "openai",
    "profile": "prod_openai"
  },
  "profile_id": "prod_openai",
  "provider": "openai",
  "auth_method": "api_key",
  "stored": true
}
```

<ParamField body="realm_id" type="string" required>
  Realm containing the binding.
</ParamField>

<ParamField body="binding_id" type="string" required>
  Binding whose configured auth profile will receive the stored credential.
</ParamField>

<ParamField body="profile_id" type="string | null">
  Optional explicit profile override for the binding.
</ParamField>

<ParamField body="provider" type="string" required>
  Provider expected from the resolved auth profile.
</ParamField>

<ParamField body="auth_method" type="string" required>
  Stored-secret method: `"api_key"`, `"azure_api_key"`, or `"static_bearer"`.
</ParamField>

<ParamField body="secret" type="string" required>
  Secret material to persist in the token store.
</ParamField>

### GET /auth/bindings/\{binding\_id}

Read the auth profile resolved by a binding.

<ParamField path="binding_id" type="string" required>
  Binding to resolve.
</ParamField>

<ParamField query="realm_id" type="string" required>
  Realm containing the binding.
</ParamField>

<ParamField query="profile_id" type="string | null">
  Optional explicit profile override for the binding.
</ParamField>

### DELETE /auth/bindings/\{binding\_id}

Clear stored credentials for the resolved binding-scoped auth profile.

<ParamField path="binding_id" type="string" required>
  Binding whose stored credentials should be cleared.
</ParamField>

<ParamField query="realm_id" type="string" required>
  Realm containing the binding.
</ParamField>

<ParamField query="profile_id" type="string | null">
  Optional explicit profile override for the binding.
</ParamField>

### POST /auth/bindings/\{binding\_id}/test

Resolve a binding through the provider registry and report whether credential
material or a dynamic authorizer is available.

```json Request theme={null}
{
  "realm_id": "prod",
  "profile_id": "prod_openai"
}
```

<ParamField path="binding_id" type="string" required>
  Binding to test.
</ParamField>

<ParamField body="realm_id" type="string" required>
  Realm containing the binding.
</ParamField>

<ParamField body="profile_id" type="string | null">
  Optional explicit profile override for the binding.
</ParamField>

### POST /auth/login/start

Begin a loopback OAuth login. The server owns the OAuth state and PKCE verifier.

```json Request theme={null}
{
  "provider": "anthropic",
  "redirect_uri": "http://127.0.0.1:53682/callback"
}
```

### POST /auth/login/complete

Complete a loopback OAuth login and store the resulting tokens under an explicit
binding-scoped `AuthBindingRef`.

```json Request theme={null}
{
  "provider": "anthropic",
  "code": "provider-code",
  "state": "state-from-start",
  "redirect_uri": "http://127.0.0.1:53682/callback",
  "realm_id": "prod",
  "binding_id": "anthropic",
  "profile_id": "claude_oauth"
}
```

<ParamField body="realm_id" type="string" required>
  Realm containing the binding.
</ParamField>

<ParamField body="binding_id" type="string" required>
  Binding that owns the stored OAuth tokens.
</ParamField>

<ParamField body="profile_id" type="string | null">
  Optional explicit profile override for the binding.
</ParamField>

### POST /auth/login/device/start

Begin a device-code OAuth login.

```json Request theme={null}
{
  "provider": "google"
}
```

### POST /auth/login/device/complete

Poll one device-code OAuth completion attempt and, when ready, store the tokens
under an explicit binding-scoped `AuthBindingRef`.

```json Request theme={null}
{
  "provider": "google",
  "device_code": "device-code-from-start",
  "realm_id": "prod",
  "binding_id": "google",
  "profile_id": "gemini_oauth"
}
```

Returns `202` with `{ "state": "pending" }` when the provider has not finished,
`429` with `{ "state": "slow_down" }` when the caller should back off, and `200`
with a `ready` payload after tokens are persisted.

### GET /auth/bindings/\{binding\_id}/status

Read binding-scoped auth status. The response includes the flattened binding
identity plus `profile_id`, `provider`, `auth_method`, public state,
expiration, refresh timestamp, account ID, and refresh-token presence.

<ParamField path="binding_id" type="string" required>
  Binding whose status should be read.
</ParamField>

<ParamField query="realm_id" type="string" required>
  Realm containing the binding.
</ParamField>

<ParamField query="profile_id" type="string | null">
  Optional explicit profile override for the binding.
</ParamField>

### POST /auth/bindings/\{binding\_id}/logout

Clear stored credentials for the binding and publish the auth lifecycle release.

<ParamField path="binding_id" type="string" required>
  Binding to log out.
</ParamField>

<ParamField query="realm_id" type="string" required>
  Realm containing the binding.
</ParamField>

<ParamField query="profile_id" type="string | null">
  Optional explicit profile override for the binding.
</ParamField>

### GET /realms

List configured realm summaries.

### GET /realms/\{id}

Read one realm connection set.

### POST /comms/send

Push a canonical comms command into a running session. Requires the `comms` feature.

<CodeGroup>
  ```json Request theme={null}
  {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "kind": "peer_message",
    "to": "reviewer",
    "body": "Please review the latest diff",
    "handling_mode": "queue",
    "source": "ci-pipeline"
  }
  ```

  ```json Response (202 Accepted) theme={null}
  {"queued": true}
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to dispatch the comms command to.
</ParamField>

<ParamField body="kind" type="string" required>
  Command kind: `"input"`, `"peer_message"`, `"peer_lifecycle"`, `"peer_request"`, or `"peer_response"`.
</ParamField>

<ParamField body="to" type="string | null">
  Peer name to send to (required for `peer_message`, `peer_lifecycle`, `peer_request`, and `peer_response`).
</ParamField>

<ParamField body="body" type="string | null">
  Message body (required for `input` and `peer_message`).
</ParamField>

<ParamField body="handling_mode" type="string | null">
  Handling mode for `input`, `peer_message`, `peer_request`, and `peer_response`. Local callers must provide `"queue"` or `"steer"`.
</ParamField>

<Note>
  `peer_message` is the default collaboration primitive. `peer_lifecycle` is a one-way topology notification. `peer_request` is only a structured ask with correlated replies.
</Note>

<ParamField body="lifecycle_kind" type="string | null">
  Lifecycle event kind: `"mob.peer_added"`, `"mob.peer_retired"`, or `"mob.peer_unwired"` (required for `peer_lifecycle`).
</ParamField>

<ParamField body="intent" type="string | null">
  Request intent (required for `peer_request`, e.g. `"review"`, `"delegate"`).
</ParamField>

<ParamField body="params" type="object | null">
  Request parameters (optional for `peer_request`).
</ParamField>

<ParamField body="in_reply_to" type="string | null">
  ID of the request being responded to (required for `peer_response`).
</ParamField>

<ParamField body="status" type="string | null">
  Response status: `"accepted"`, `"completed"`, or `"failed"` (for `peer_response`).
</ParamField>

<ParamField body="result" type="object | null">
  Response result data (optional for `peer_response`).
</ParamField>

<ParamField body="source" type="string | null">
  Optional source label.
</ParamField>

<ParamField body="stream" type="string | null">
  Optional input stream mode: `"none"` or `"reserve_interaction"` (for `input` and `peer_request`).
</ParamField>

<ParamField body="allow_self_session" type="bool | null">
  Allow the session to send a message to itself (default: false).
</ParamField>

<Note>
  Unlike `POST /sessions/{id}/external-events` (which uses `RKAT_WEBHOOK_SECRET`
  header auth), `/comms/send` has **no authentication**. It is intended for
  trusted internal callers that already know the session ID.
</Note>

### GET /comms/peers

List discoverable peers for a session. Requires the `comms` feature.

<ParamField query="session_id" type="string" required>
  Session ID to query peers for.
</ParamField>

```json Response theme={null}
{
  "peers": [
    {"name": "reviewer", "address": "127.0.0.1:4201"},
    {"name": "coordinator", "address": "inproc"}
  ]
}
```

## Error responses

All errors are returned as JSON with an HTTP status code:

```json theme={null}
{
  "error": "Human-readable error message",
  "code": "ERROR_CODE"
}
```

| HTTP Status | Code                      | When                                                               |
| ----------- | ------------------------- | ------------------------------------------------------------------ |
| 400         | `BAD_REQUEST`             | Invalid parameters, session ID mismatch, keep\_alive without comms |
| 403         | `HOOK_DENIED`             | Hook blocked the operation                                         |
| 404         | `SESSION_NOT_FOUND`       | Session does not exist                                             |
| 404         | `SKILL_NOT_FOUND`         | Requested skill does not exist                                     |
| 409         | `SESSION_BUSY`            | Turn already in progress                                           |
| 409         | `SESSION_NOT_RUNNING`     | Session not in running state                                       |
| 422         | `SKILL_RESOLUTION_FAILED` | Skill resolution error                                             |
| 429         | `BUDGET_EXHAUSTED`        | Resource limits reached                                            |
| 500         | `AGENT_ERROR`             | LLM provider error, tool dispatch failure, agent loop error        |
| 500         | `INTERNAL_ERROR`          | Store initialization failure, unexpected server error              |
| 501         | `CAPABILITY_UNAVAILABLE`  | Required capability not compiled in or disabled                    |
| 502         | `PROVIDER_ERROR`          | LLM provider issue (missing key, auth failure)                     |

<CodeGroup>
  ```json Session not found theme={null}
  {
    "error": "Session not found: 01936f8a-7b2c-7000-8000-000000000099",
    "code": "SESSION_NOT_FOUND"
  }
  ```

  ```json Bad request theme={null}
  {
    "error": "Session ID mismatch: path=abc body=def",
    "code": "BAD_REQUEST"
  }
  ```
</CodeGroup>

## Notes

<Note>
  Keep-alive mode requires `keep_alive: true` and a `comms_name`. If `keep_alive` is
  requested but the binary was not compiled with comms support, the server
  returns a `BAD_REQUEST` error.
</Note>

<Note>
  `POST /sessions/{id}/external-events` queues a runtime-backed external event. It is a queue-only admission path, not a second direct execution loop.
</Note>

<Note>
  `hooks_override` allows per-request hook overrides including adding extra hook
  entries and disabling specific hooks by ID. See [Hooks](/guides/hooks) for the
  `HookRunOverrides` schema.
</Note>
