# REST

Base URL `https://api.brynth.ai`. Every request carries `Authorization: Bearer brk_live_…`, except `POST /v1/auth/start`, `POST /v1/auth/poll`, `POST /v1/projects` and the OpenAPI document. Bodies are JSON objects, except `PUT /v1/kv/{key}` and `PUT /v1/files/{path}`: see [Disk](#disk). The OpenAPI 3.1 document is at `https://api.brynth.ai/v1/openapi.json`.

Errors have the shape `{"error": {"code", "message", "hint", "details"}}`, where `hint` can be `null` and `details` is present only when there is more to say, with the HTTP status of the [error code](/reference/errors.md). Responses carry an `X-Request-Id` header.

## Sign-in and projects

| Route | MCP tool |
|---|---|
| `POST /v1/auth/start` | |
| `POST /v1/auth/poll` | |
| `POST /v1/projects` | |
| `GET /v1/whoami` | `whoami` |
| `POST /v1/session/hello` | `session_hello` |
| `GET /v1/keys` | |
| `POST /v1/keys` | |
| `DELETE /v1/keys/{id}` | |

## Inbox

| Route | MCP tool |
|---|---|
| `GET /v1/sources` | `source_list` |
| `POST /v1/sources` | `source_add` |
| `DELETE /v1/sources/{name}` | `source_remove` |
| `GET /v1/inbox` | `inbox_list` |
| `GET /v1/inbox/wait` | `inbox_wait` |
| `POST /v1/inbox/claim` | `inbox_claim` |
| `POST /v1/inbox/ack` | `inbox_ack` |
| `POST /v1/inbox/nack` | `inbox_nack` |
| `GET /v1/routes` | |
| `POST /v1/routes` | |
| `DELETE /v1/routes/{id}` | |

## Requests, channels and policy

| Route | MCP tool |
|---|---|
| `POST /v1/requests` | `ask_approval`, `ask_human` |
| `GET /v1/requests/{id}` | `request_status` |
| `POST /v1/request-callback-secret` | |
| `GET /v1/channels` | `channel_list` |
| `POST /v1/channels` | `channel_add` |
| `DELETE /v1/channels/{id}` | `channel_remove` |
| `GET /v1/policy` | `policy_get` |
| `PUT /v1/policy` | `policy_set` |

## Disk

| Route | MCP tool |
|---|---|
| `GET /v1/kv` | `kv_list` |
| `GET /v1/kv/{key}` | `kv_get` |
| `PUT /v1/kv/{key}` | `kv_set` |
| `DELETE /v1/kv/{key}` | `kv_delete` |
| `GET /v1/state/{task_id}` | `state_load` |
| `PUT /v1/state/{task_id}` | `state_save` |
| `GET /v1/files` | `file_list` |
| `GET /v1/files/{path}` | `file_get` |
| `PUT /v1/files/{path}` | `file_put` |
| `DELETE /v1/files/{path}` | `file_delete` |
| `POST /v1/file-urls` | `file_url` |

Two disk routes do not take the tool's parameters as a JSON object:

- `PUT /v1/kv/{key}`: the whole body is the value, any JSON text up to 256 KB, and `ttl_seconds` goes in the query string (`PUT /v1/kv/orders%2F1042?ttl_seconds=60`). A body like `{"value": …, "ttl_seconds": 60}` is stored as it is, as the value, with no expiry.
- `PUT /v1/files/{path}`: the body is the file's bytes, up to 1 MB, and its `Content-Type` becomes the file's type (`application/octet-stream` when absent). For a larger file, up to 100 MB, send `Content-Type: application/vnd.brynth.upload+json` with `{"upload": {"size": 5242880, "content_type": "application/pdf"}}`: PUT the bytes to the `upload_url` of the answer with exactly its `upload_headers`, within 15 minutes, then send `{"complete": "<upload_id>"}` on the same route, with the same `Content-Type`.

In `{key}` and `{path}`, write `/` as `%2F`: `orders/1042` is `/v1/kv/orders%2F1042`, `reports/q3.pdf` is `/v1/files/reports%2Fq3.pdf`.

## Sending and ledger

| Route | MCP tool |
|---|---|
| `POST /v1/emails` | `email_send` |
| `GET /v1/ledger` | `ledger_query` |
| `POST /v1/ledger/verify` | `ledger_verify` |

Webhook sources receive at `POST https://hooks.brynth.ai/w/{token}`, the URL that `source_add` returns, with no key: see [Events and the envelope](/concepts/events.md).

Apart from these two bodies, parameters and limits are the same as the MCP tools: see [MCP tools](/reference/mcp-tools.md) and [Limits](/reference/limits.md).
