# API reference

{/* // THE /v1 CONTRACT */}

Postern exposes a small, JSON, OpenAPI-described REST API under `/v1`. The SDKs and the MCP server map
1:1 onto it, so anything you can do with them you can do with a raw HTTP request.
**Rolling out — build against the contract today:** The Postern Go API is rolling out. This reference is the published `/v1` contract; the
  [SDK](https://docs.agents.mszazu.com/quickstart/mailbox-in-one-call/) and [MCP server](https://docs.agents.mszazu.com/mcp/overview/) ship a deterministic
  `mock` transport so you can build and test against these shapes now. Set `POSTERN_API_BASE_URL=mock`
  (SDK) or `POSTERN_MOCK=1` (MCP) to run fully offline. The wire shapes are stable; the live base URL
  flips on at GA.

## Base URL

```bash frame="terminal"
export POSTERN_API_BASE_URL="https://api.agents.mszazu.com"
```

Override it with the `POSTERN_API_BASE_URL` environment variable everywhere — SDK, MCP, and your own
clients. Use `mock` to run against fixtures.

## Authentication

Every endpoint except `POST /v1/enroll` requires a **scoped agent key** as a bearer token:

```bash frame="terminal"
curl -H "Authorization: Bearer pk_agent_…" "$POSTERN_API_BASE_URL/v1/inboxes"
```

The `customer_id` for a request is derived from the key — never from client input — so the API is
tenant-scoped by construction. See [Authentication & keys](https://docs.agents.mszazu.com/quickstart/authentication/).

## Endpoints

| Method & path | Does |
|---|---|
| `POST /v1/enroll` | Redeem an enrollment key for a scoped agent key. |
| `POST /v1/inboxes` | Create a mailbox. |
| `GET /v1/inboxes` | List mailboxes. |
| `GET /v1/inboxes/{addr}` | Get one mailbox. |
| `DELETE /v1/inboxes/{addr}` | Delete a mailbox (WildDuck + ACS teardown). |
| `POST /v1/inboxes/{addr}/send` | Send an email. |
| `GET /v1/inboxes/{addr}/messages` | List messages in a mailbox. |
| `GET /v1/inboxes/{addr}/threads` | List conversation threads. |
| `POST /v1/inboxes/{addr}/wait` | Block until a matching message arrives (`wait_for_email`). |
| `GET /v1/messages/{id}` | Get one message. |
| `POST /v1/messages/{id}/reply` | Reply in-thread. |
| `GET /v1/threads/{id}` | Get one thread. |
| `POST /v1/webhooks` | Register an HMAC-signed inbound webhook. |

## Conventions

| Topic | Rule |
|---|---|
| **Content type** | Requests and responses are `application/json`. |
| **IDs** | Prefixed and opaque: `inbox_…`, `msg_…`, `thread_…`, `agent_…`. |
| **Addresses in paths** | URL-encode the mailbox address in `{addr}` (`agent7%40x4p.mszazu.com`). |
| **Pagination** | List responses carry `next_cursor`; pass it back as `?cursor=`. Use `?limit=`. |
| **Idempotency** | Pass `client_id` to create and `idempotency_key` to send/reply — retries won't duplicate. |
| **Timestamps** | ISO 8601 UTC (`2026-06-12T18:04:11Z`). |
| **Errors** | Typed, with a stable `error.code`. See [Errors](https://docs.agents.mszazu.com/api/errors/). |
| **Request id** | Every response carries `X-Postern-Request-Id` for support. |

## Rate limits

Limits are published and designed not to punish fan-out across many mailboxes. A `429` carries a
`Retry-After` header. See [Rate limits & quotas](https://docs.agents.mszazu.com/operating/limits/). fan-out friendly

## Per-endpoint reference

- [Enrollment](https://docs.agents.mszazu.com/api/enrollment/) — `POST /v1/enroll`
- [Inboxes](https://docs.agents.mszazu.com/api/inboxes/) — create / list / get / delete / send
- [Messages & threads](https://docs.agents.mszazu.com/api/messages-and-threads/) — list / get / reply
- [wait_for_email](https://docs.agents.mszazu.com/api/wait/) — `POST /v1/inboxes/{addr}/wait`
- [Webhooks](https://docs.agents.mszazu.com/api/webhooks/) — `POST /v1/webhooks` + signature verification
- [Errors](https://docs.agents.mszazu.com/api/errors/) — status codes and `error.code`s