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

# API reference

> Every Agent Mailbox endpoint, grouped by resource.

**Base URL** — `https://agent.waterr.ai`
**Auth** — `Authorization: Bearer <key>` on every request. See [Authentication](/agent-mailbox/authentication).

## Inboxes

| Method   | Path                     | Purpose                         |
| -------- | ------------------------ | ------------------------------- |
| `POST`   | `/v0/inboxes`            | Create an inbox                 |
| `GET`    | `/v0/inboxes`            | List inboxes in scope           |
| `GET`    | `/v0/inboxes/{inbox_id}` | Read one                        |
| `PATCH`  | `/v0/inboxes/{inbox_id}` | Update `display_name`           |
| `DELETE` | `/v0/inboxes/{inbox_id}` | Delete, with its whole database |

## Messages

| Method  | Path                                                                       | Purpose              |
| ------- | -------------------------------------------------------------------------- | -------------------- |
| `POST`  | `/v0/inboxes/{inbox_id}/messages/send`                                     | Send a message       |
| `POST`  | `/v0/inboxes/{inbox_id}/messages/{message_id}/reply`                       | Reply on the thread  |
| `POST`  | `/v0/inboxes/{inbox_id}/messages/{message_id}/forward`                     | Forward              |
| `GET`   | `/v0/inboxes/{inbox_id}/messages`                                          | List messages        |
| `GET`   | `/v0/inboxes/{inbox_id}/messages/{message_id}`                             | Read one             |
| `GET`   | `/v0/inboxes/{inbox_id}/messages/{message_id}/raw`                         | Original MIME source |
| `PATCH` | `/v0/inboxes/{inbox_id}/messages/{message_id}`                             | Update labels        |
| `GET`   | `/v0/inboxes/{inbox_id}/messages/{message_id}/attachments/{attachment_id}` | Fetch an attachment  |

## Threads

| Method   | Path                                         | Purpose                          |
| -------- | -------------------------------------------- | -------------------------------- |
| `GET`    | `/v0/inboxes/{inbox_id}/threads`             | List threads in one inbox        |
| `GET`    | `/v0/inboxes/{inbox_id}/threads/{thread_id}` | Read a thread with its messages  |
| `GET`    | `/v0/threads`                                | List across every inbox in scope |
| `DELETE` | `/v0/inboxes/{inbox_id}/threads/{thread_id}` | Delete a thread                  |

## Drafts

| Method   | Path                                            | Purpose                   |
| -------- | ----------------------------------------------- | ------------------------- |
| `POST`   | `/v0/inboxes/{inbox_id}/drafts`                 | Create a draft            |
| `GET`    | `/v0/inboxes/{inbox_id}/drafts`                 | List drafts               |
| `GET`    | `/v0/inboxes/{inbox_id}/drafts/{draft_id}`      | Read one                  |
| `PATCH`  | `/v0/inboxes/{inbox_id}/drafts/{draft_id}`      | Edit, including `send_at` |
| `POST`   | `/v0/inboxes/{inbox_id}/drafts/{draft_id}/send` | Send it                   |
| `DELETE` | `/v0/inboxes/{inbox_id}/drafts/{draft_id}`      | Discard                   |

## Search and events

| Method | Path                               | Purpose                                       |
| ------ | ---------------------------------- | --------------------------------------------- |
| `GET`  | `/v0/inboxes/{inbox_id}/search?q=` | Full-text search in one inbox                 |
| `GET`  | `/v0/inboxes/{inbox_id}/events`    | Recent events for an inbox                    |
| `GET`  | `/v0/metrics`                      | Service metrics                               |
| `GET`  | `/health`                          | Health check — the only unauthenticated route |

## Lists

| Method   | Path                                                      | Purpose      |
| -------- | --------------------------------------------------------- | ------------ |
| `GET`    | `/v0/inboxes/{inbox_id}/lists/{direction}/{type}`         | List entries |
| `POST`   | `/v0/inboxes/{inbox_id}/lists/{direction}/{type}`         | Add an entry |
| `GET`    | `/v0/inboxes/{inbox_id}/lists/{direction}/{type}/{entry}` | Read one     |
| `DELETE` | `/v0/inboxes/{inbox_id}/lists/{direction}/{type}/{entry}` | Remove       |

`direction` is `send`, `receive` or `reply`. `type` is `allow` or `block`.

## Webhooks

| Method   | Path                                          | Purpose                                    |
| -------- | --------------------------------------------- | ------------------------------------------ |
| `POST`   | `/v0/webhooks`                                | Create — returns the signing `secret` once |
| `GET`    | `/v0/webhooks`                                | List                                       |
| `GET`    | `/v0/webhooks/{webhook_id}`                   | Read one                                   |
| `PATCH`  | `/v0/webhooks/{webhook_id}`                   | Update url, events, or `enabled`           |
| `DELETE` | `/v0/webhooks/{webhook_id}`                   | Delete                                     |
| `GET`    | `/v0/webhooks/{webhook_id}/deliveries`        | Delivery history                           |
| `POST`   | `/v0/webhooks/deliveries/{delivery_id}/retry` | Force a retry                              |

## Pods

| Method   | Path                        | Purpose                                 |
| -------- | --------------------------- | --------------------------------------- |
| `POST`   | `/v0/pods`                  | Create a pod                            |
| `GET`    | `/v0/pods`                  | List pods                               |
| `GET`    | `/v0/pods/{pod_id}`         | Read one                                |
| `DELETE` | `/v0/pods/{pod_id}`         | Delete — refused while it holds inboxes |
| `POST`   | `/v0/pods/{pod_id}/inboxes` | Create an inbox in the pod              |
| `GET`    | `/v0/pods/{pod_id}/inboxes` | List its inboxes                        |
| `GET`    | `/v0/pods/{pod_id}/threads` | List threads across the pod             |

## Domains

| Method   | Path                             | Purpose                                |
| -------- | -------------------------------- | -------------------------------------- |
| `POST`   | `/v0/domains`                    | Register a domain, get its DNS records |
| `GET`    | `/v0/domains`                    | List                                   |
| `GET`    | `/v0/domains/{domain_id}`        | Read one, with verification state      |
| `POST`   | `/v0/domains/{domain_id}/verify` | Re-check DNS                           |
| `DELETE` | `/v0/domains/{domain_id}`        | Remove                                 |

## API keys

Admin key only.

| Method   | Path                    | Purpose                                    |
| -------- | ----------------------- | ------------------------------------------ |
| `POST`   | `/v0/api-keys`          | Mint a scoped key — returns `api_key` once |
| `GET`    | `/v0/api-keys`          | List metadata                              |
| `DELETE` | `/v0/api-keys/{key_id}` | Revoke                                     |

## WebSocket

| Path                        | Purpose                           |
| --------------------------- | --------------------------------- |
| `/v0/ws`                    | Hub socket with subscribe filters |
| `/v0/inboxes/{inbox_id}/ws` | One socket for one inbox          |

Both accept `?api_key=` since browsers cannot set headers on a WebSocket.

## Conventions

### Pagination

List endpoints take `limit` and a cursor. Responses are wrapped:

```json theme={null}
{
  "data": [ "..." ],
  "count": 50,
  "next_cursor": "eyJrIjoi...",
  "next_page_token": "eyJrIjoi..."
}
```

`next_cursor` and `next_page_token` are the same value under two names. Pass either back as `?cursor=` or `?page_token=`. Both aliases exist so client libraries written against other mailbox APIs work unchanged. When the pagination fields are absent, you have reached the end.

### Identifiers

| Prefix | Resource         |
| ------ | ---------------- |
| `msg_` | Message          |
| `thd_` | Thread           |
| `dft_` | Draft            |
| `att_` | Attachment       |
| `whk_` | Webhook          |
| `dlv_` | Webhook delivery |
| `evt_` | Event            |
| `pod_` | Pod              |
| `dom_` | Domain           |
| `key_` | API key          |

Inboxes are the exception: an inbox's identifier is its email address.

### Errors

Failures return a consistent body, and the `code` is stable — match on it rather than on the message text.

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "username may not contain '+', which is reserved for thread tokens"
  }
}
```

| Code                | Status | Meaning                                              |
| ------------------- | ------ | ---------------------------------------------------- |
| `unauthorized`      | 401    | Missing or invalid key                               |
| `forbidden`         | 403    | Valid key, out of scope — or a list refused the send |
| `not_found`         | 404    | No such resource                                     |
| `conflict`          | 409    | State prevents it, e.g. deleting a non-empty pod     |
| `payload_too_large` | 413    | Over the size limit                                  |
| `validation_error`  | 422    | Malformed or invalid input                           |
| `rate_limited`      | 429    | Over 300 requests / 60s for this key                 |
| `internal_error`    | 500    | Unexpected failure                                   |
| `not_implemented`   | 501    | Not available on this deployment                     |
| `provider_error`    | 502    | The mail transport failed                            |

Some errors carry a `details` object with structured context — a list refusal names the direction and recipient.
