# API conventions

Source: https://docs.simail.ai/api/

> Base URL, authentication, JSON, pagination, idempotency, errors, request IDs, scopes and the OpenAPI document — rules that apply to every simail endpoint.

## Base URL and versions

```text
https://api.simail.ai/v0
```

The version is in the path. Within `v0` we only add things: new endpoints, new optional request
fields and new response fields. Ignore fields you do not know. Paths and field names follow the
common agent-mailbox API shape, so existing clients for that shape work; simail-only features are
separate endpoints or separate fields.

The machine-readable description of every operation is the **OpenAPI 3.1** document:

`GET /v0/openapi.json`

`GET /v0/health` returns `{ "status": "ok", "service": "simail-api", "time": "…" }` without a key.

## Authentication

```http
Authorization: Bearer sk_<prefix>_<secret>
```

Every endpoint needs a key except `GET /v0/beta`, `POST /v0/agent/sign-up`, `POST /v0/waitlist`,
`GET /v0/health` and `GET /v0/openapi.json`. A missing or bad key returns `401 unauthorized`. Each
endpoint below lists the permission it needs; see [Authentication](/authentication/).

Endpoints marked **human only** answer `403 human_required` to any key: a human does that in the
console.

## Scope: not found means not yours

A key sees only objects in its scope (organization, pod or inbox). Anything outside it, including
other organizations, returns **`404 not_found`**, exactly like an object that does not exist. Inside
the scope, a missing permission returns `403 missing_permission`. Organization-level listings such as
`GET /v0/threads` are narrowed to the inboxes your key can see.

## Requests

- Request bodies are JSON (`Content-Type: application/json`); anything else returns
  `415 unsupported_media_type`.
- Bodies are limited to 1 MiB, or 5 MiB for sends that carry attachments inline
  (`413 payload_too_large`).
- Unknown fields are rejected with `400 validation_error`, which lists every problem in
  `errors: [{ path, message }]`.
- Times are ISO 8601 in UTC (`2026-10-18T17:02:11Z`).
- Inbox IDs are email addresses. Encode them in paths as they are (`@` is allowed in a path).

## Pagination

List endpoints take:

| Parameter | Notes |
|---|---|
| `limit` | 1–200, default 50 |
| `page_token` | the `next_page_token` from the previous page |
| `ascending` | `true` for oldest first; default newest first |

and return:

```json
{ "count": 2, "limit": 50, "next_page_token": "pt_…", "threads": [ ] }
```

The list key is named after the resource (`inboxes`, `threads`, `messages`, `drafts`, …).
`next_page_token` is absent on the last page. Page tokens are opaque and signed; they are bound to
your organization, your key's scope and the query, so using one with another key or other filters
returns `400 invalid_request`.

## Idempotency

Every `POST` accepts `Idempotency-Key`; sends **require** it. The same key with the same body returns
the original response; with a different body, `409 idempotency_conflict`. See
[Idempotency & duplicates](/idempotency/).

## Errors

Errors are `application/problem+json` with a stable `code`, `retryable`, a one-line `fix` and a
`request_id`; some carry a `human_message` for the human behind the agent. Every `429` and retryable
`503` has `Retry-After`. See [Errors](/errors/).

## Request IDs

Every response has an `x-request-id: req_…` header, and every error body repeats it as `request_id`.
Include it when you contact support. simail logs request metadata, never message content.

## Rate limits

600 requests per minute per key in bursts, plus daily quotas per organization. See
[Rate limits](/rate-limits/).

## Endpoint index

| Area | Endpoints |
|---|---|
| [Beta & waitlist](/api/beta/) | `/v0/beta`, `/v0/agent/sign-up`, `/v0/waitlist` |
| [Identity & organization](/api/identity/) | `/v0/auth/me`, `/v0/organizations` |
| [Inboxes](/api/inboxes/) | `/v0/inboxes`, `/v0/inboxes/{inbox_id}`, `/v0/inboxes/search` |
| [Pods](/api/pods/) | `/v0/pods`, `/v0/pods/{pod_id}/…` |
| [Threads](/api/threads/) | `/v0/inboxes/{inbox_id}/threads…`, `/v0/threads…` |
| [Messages](/api/messages/) | `/v0/inboxes/{inbox_id}/messages…` |
| [Send message](/api/send-message/) | `…/messages/send`, `…/reply`, `…/reply-all`, `…/forward` |
| [Drafts](/api/drafts/) | `/v0/inboxes/{inbox_id}/drafts…`, `/v0/drafts…` |
| [Sends](/api/sends/) | `/v0/sends/{send_id}` |
| [Approvals](/api/approvals/) | `/v0/approvals…` |
| [Locks](/api/locks/) | `…/threads/{thread_id}/lock` |
| [Labels](/api/labels/) | labels on messages and threads |
| [Lists](/api/lists/) | `/v0/lists/{direction}/{type}…` |
| [API keys](/api/api-keys/) | `/v0/api-keys…` |
| [Webhooks](/api/webhooks/) | `/v0/webhooks…` |
| [Events](/api/events/) | `/v0/events`, `/v0/inboxes/{inbox_id}/events`, `/v0/ws` |
| [Usage & metrics](/api/usage/) | `/v0/usage`, `/v0/metrics/…` |
