# Errors, limits and pagination

Error codes and what to do about each, recovery by operation, pagination and limits.

Source: https://cherami.to/docs/api/errors



{/* Generated from apps/web/openapi. Edit the contract, not this file. */}

## Error format [#error-format]

Errors have this shape:

```json
{"error":{"code":"not_found","message":"Resource not found."}}
```

Handle errors by `error.code`; `message` explains the case, and its wording can change. Include the response's `X-Request-ID` when you [report a problem](https://cherami.to/docs/guides/support).

| HTTP status  | Meaning                                                                                                                                                                                                  |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`        | The request is invalid. Correct it before sending it again.                                                                                                                                              |
| `401`        | The API key is missing, invalid or revoked; [Replace an API key](https://cherami.to/docs/guides/recovery) covers getting a new one.                                                                                        |
| `403`        | The account can't perform this operation, the inbox's sending rules block a recipient, or a signup or redemption request carries another site's `Origin`.                                                |
| `404`        | The resource doesn't exist, was deleted or belongs to another account. A reply or forward source must also be in the sending inbox.                                                                      |
| `409`        | The request conflicts with current state: an unavailable address, the inbox limit, a source that can't be replied to, a draft change or submission, an idempotency key, or a webhook change in progress. |
| `413`        | The request or message is too large.                                                                                                                                                                     |
| `415`        | Send `Content-Type: application/json`.                                                                                                                                                                   |
| `429`        | Too many signup or redemption requests, or not enough sending allowance. Wait for `Retry-After` when present.                                                                                            |
| `500`, `503` | The service failed. A write may still have happened: recover by operation, below.                                                                                                                        |

## Error codes [#error-codes]

Handle a code not listed here by its HTTP status and the operation's recovery.

| Code                             | HTTP status | Meaning and recovery                                                                                                                                                                                                                                                                                                                                                                                  |
| -------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_json`                   | 400         | Send a valid UTF-8 JSON object, not an array or scalar.                                                                                                                                                                                                                                                                                                                                               |
| `invalid_request`                | 400         | The signup or redemption body has an unsupported field. Signup takes an empty object; redemption takes `phrase` and, optionally, `discovery_source` and `intended_use`.                                                                                                                                                                                                                               |
| `invalid_claim`                  | 400         | The phrase is invalid, expired or already used. Ask the human for a new one: they choose Get the message under Connect an agent at [https://cherami.to/account](https://cherami.to/account).                                                                                                                                                                                                          |
| `invalid_local_part`             | 400         | Use 1–64 letters, digits, hyphens or underscores for `local_part`, starting and ending with a letter or digit.                                                                                                                                                                                                                                                                                        |
| `invalid_inbox`                  | 400         | Creation takes only `local_part`, `name`, `sender_name` and `idempotency_key`; an edit takes at least one of `name` and `sender_name`, and nothing else.                                                                                                                                                                                                                                              |
| `invalid_name`                   | 400         | Use plain text without control characters, at most 256 UTF-8 bytes after trimming. The message names the field.                                                                                                                                                                                                                                                                                       |
| `invalid_draft`                  | 400         | Correct the draft field, `source` or listing parameter the message names.                                                                                                                                                                                                                                                                                                                             |
| `invalid_message`                | 400         | Correct the recipients, subject, body, `in_reply_to`, attachments or reply and forward fields as the message describes.                                                                                                                                                                                                                                                                               |
| `invalid_idempotency_key`        | 400         | Use 1–128 ASCII letters, digits, hyphens or underscores.                                                                                                                                                                                                                                                                                                                                              |
| `invalid_labels`                 | 400         | Correct the label name, label change, filter group or discovery prefix the message names.                                                                                                                                                                                                                                                                                                             |
| `invalid_message_ids`            | 400         | Supply 1–100 valid message IDs for a bulk label update.                                                                                                                                                                                                                                                                                                                                               |
| `invalid_feedback`               | 400         | Send a nonempty plain-text `body` and, optionally, a nonempty single-line `title`, with no other fields.                                                                                                                                                                                                                                                                                              |
| `invalid_limit`                  | 400         | Use an integer from 1 to 100.                                                                                                                                                                                                                                                                                                                                                                         |
| `invalid_cursor`                 | 400         | Send a cursor only to the listing, filters and order that returned it, or restart from the first page.                                                                                                                                                                                                                                                                                                |
| `invalid_search`                 | 400         | Correct the search terms, filters, timestamps or ordering the message names. See [search](https://cherami.to/docs/guides/search).                                                                                                                                                                                                                                                                                       |
| `invalid_webhook`                | 400         | Adding a webhook takes only `url`, `event_types` and `inbox_ids`.                                                                                                                                                                                                                                                                                                                                     |
| `invalid_webhook_url`            | 400         | Use an https URL with a public hostname (not an IP address), without credentials or a fragment.                                                                                                                                                                                                                                                                                                       |
| `invalid_webhook_event_types`    | 400         | Supply a nonempty array of `message.received`, `message.sent` or both.                                                                                                                                                                                                                                                                                                                                |
| `invalid_webhook_inbox_ids`      | 400         | Supply `inbox_ids` as an array of at most 10 inbox IDs, or an empty array for every inbox.                                                                                                                                                                                                                                                                                                            |
| `unknown_webhook_inbox`          | 400         | An `inbox_ids` entry is not a live inbox of this account. Remove the IDs listed in `error.details.inbox_ids`.                                                                                                                                                                                                                                                                                         |
| `invalid_webhook_update`         | 400         | Supply at least one of `event_types`, `inbox_ids` and `enabled`, with `enabled` as a boolean.                                                                                                                                                                                                                                                                                                         |
| `invalid_webhook_settings`       | 400         | The settings were refused, and nothing changed. Read the webhook, then send corrected values.                                                                                                                                                                                                                                                                                                         |
| `invalid_webhook_rotation`       | 400         | Send an empty body or only a boolean `immediate`.                                                                                                                                                                                                                                                                                                                                                     |
| `invalid_webhook_headers`        | 400         | Correct the header change the message names. Names are HTTP tokens of at most 128 characters, other than `Host`, `Content-Type`, `Content-Length`, `User-Agent` and names starting `svix-` or `webhook-`; values are printable and at most 1024 characters; `set` and `remove` take at most 20 entries each.                                                                                          |
| `invalid_webhook_event`          | 400         | Use a Cherami event ID (a UUID): a delivered payload's `id`, or one listed by recent events.                                                                                                                                                                                                                                                                                                          |
| `unauthorized`                   | 401         | Send a valid API key as `Authorization: Bearer <key>`. If the key stopped working, get a new one through [recovery](https://cherami.to/docs/guides/recovery).                                                                                                                                                                                                                                                           |
| `recipient_not_allowed`          | 403         | Nothing was sent: the inbox's [sending rules](https://cherami.to/account/sending-rules) block one or more recipients, which the message lists. Use allowed recipients, or ask the account owner to allow them; do not send through another inbox to get around the rules.                                                                                                                                               |
| `operation_not_allowed`          | 403         | Sending, or deletion and restore, is turned off for this account; the message says which. Contact [hello@cherami.to](mailto:hello@cherami.to).                                                                                                                                                                                                                                                        |
| `origin_not_allowed`             | 403         | Signup and redemption refuse requests carrying another site's `Origin` header. Call them from the agent's runtime, not from a web page.                                                                                                                                                                                                                                                               |
| `not_found`                      | 404         | The resource does not exist or does not belong to this account. A reply or forward source must also be in the sending inbox.                                                                                                                                                                                                                                                                          |
| `address_unavailable`            | 409         | The address is in use, reserved or retired. Choose another `local_part`.                                                                                                                                                                                                                                                                                                                              |
| `inbox_limit_reached`            | 409         | All inbox slots are in use; `error.details.allowance` has the counts. Use an existing inbox, or [request more slots](https://cherami.to/docs/guides/support) rather than deleting an inbox still in use.                                                                                                                                                                                                                |
| `reply_not_ready`                | 409         | The source cannot be replied to or forwarded yet. A received message must have `processing_status` `ready`; a sent message must have `status` `accepted`.                                                                                                                                                                                                                                             |
| `reply_recipients_unavailable`   | 409         | No recipients remain once the sending inbox is excluded. Send a new message to recipients the task authorizes.                                                                                                                                                                                                                                                                                        |
| `reply_headers_unavailable`      | 409         | The target has no usable Message-ID to reply to. Send a new message without `in_reply_to`.                                                                                                                                                                                                                                                                                                            |
| `draft_submitted`                | 409         | The draft's content froze on its first send attempt, so it cannot be edited. Read the sent message named by the draft's `sent_message_id` for the outcome; to send different content, create a new draft.                                                                                                                                                                                             |
| `draft_busy`                     | 409         | Another change to this draft landed first. Read the draft, then repeat the edit or send if it still applies.                                                                                                                                                                                                                                                                                          |
| `draft_result_unavailable`       | 409         | The draft was already submitted and its sent message has since been deleted; nothing was sent again. If that message is still in Trash, restore it with the draft's `sent_message_id` to read the outcome.                                                                                                                                                                                            |
| `idempotency_conflict`           | 409         | The key was first used with different input. To recover that request, repeat it with its original inbox and payload; a different request needs its own key.                                                                                                                                                                                                                                           |
| `idempotency_result_unavailable` | 409         | The key already created an inbox, draft or send whose result has since been deleted. Nothing new was created or sent; do not repeat the request under a new key. If a sent message is still in Trash, restore it to read the outcome.                                                                                                                                                                 |
| `webhook_operation_in_progress`  | 409         | Another change to this webhook is in progress. Retry in a minute.                                                                                                                                                                                                                                                                                                                                     |
| `webhook_endpoint_missing`       | 409         | This webhook no longer receives events and cannot be changed. Delete it and add it again; the new webhook has a new ID and signing secret.                                                                                                                                                                                                                                                            |
| `body_too_large`                 | 413         | Reduce the JSON request to the operation's body limit.                                                                                                                                                                                                                                                                                                                                                |
| `message_too_large`              | 413         | The message exceeds 5 MiB counting text, HTML and base64 attachment content, or a forwarded original has more than 32 attachments. Reduce the content or attachments; for a forward, set `include_attachments` to false or attach selected files to a new message.                                                                                                                                    |
| `unsupported_media_type`         | 415         | Send `Content-Type: application/json`.                                                                                                                                                                                                                                                                                                                                                                |
| `rate_limited`                   | 429         | Too many signup or redemption requests from this network. Wait for `Retry-After`, then try again.                                                                                                                                                                                                                                                                                                     |
| `outbound_limit_reached`         | 429         | The rolling 24-hour sending allowance cannot cover this message, at one charge per recipient. With `reason` `temporary_exhaustion`, retry at `sufficient_capacity_at`; with `message_exceeds_allowance`, the message needs more than the whole allowance: reduce its recipients or [request a higher allowance](https://cherami.to/docs/guides/support). See [sending allowance](https://cherami.to/docs/api/sending/get-outbound-quota). |
| `internal_error`                 | 500         | The operation failed. Retry a read; after a write the change may have happened, so recover as [retry by operation](https://cherami.to/docs/api/errors#retry-by-operation) describes.                                                                                                                                                                                                                                    |
| `content_unavailable`            | 503         | Stored content could not be read. Retry later.                                                                                                                                                                                                                                                                                                                                                        |
| `outbound_unavailable`           | 503         | The request failed. If it was a send, reply or forward, its outcome is unknown: repeat it with the same idempotency key and unchanged payload within 24 hours of the first request; without a key, check sent messages before sending again. Retry a read.                                                                                                                                            |
| `draft_unavailable`              | 503         | The draft request failed. Repeat a draft send with the same draft ID: it never sends twice. Repeat a creation with its original idempotency key and payload; without a key, list drafts first. Read the draft before repeating an edit. Retry a read or a deletion.                                                                                                                                   |
| `feedback_unavailable`           | 503         | The feedback could not be confirmed and may already have been recorded. Repeat it only if a duplicate is acceptable.                                                                                                                                                                                                                                                                                  |
| `webhooks_unavailable`           | 503         | Webhook management failed. If it was a change, it may have been applied: list or read the webhooks before repeating it.                                                                                                                                                                                                                                                                               |
| `webhook_provider_unavailable`   | 503         | The request could not be completed, and nothing changed. Retry later.                                                                                                                                                                                                                                                                                                                                 |
| `webhook_creation_uncertain`     | 503         | The new webhook was not confirmed and is listed as `pending`. Within a few minutes the webhook in `error.details.webhook_id` becomes `active`, and you [retrieve its secret](https://cherami.to/docs/api/webhooks/get-webhook-secret), or leaves the list, and you add it again.                                                                                                                                        |
| `webhook_secret_unavailable`     | 503         | The signing secret could not be read; retry the read later. When `error.details.webhook_id` is present the webhook was added: retrieve its secret rather than adding it again.                                                                                                                                                                                                                        |
| `webhook_rotation_uncertain`     | 503         | The rotation was not confirmed and finishes within a few minutes. Then [read the webhook](https://cherami.to/docs/api/webhooks/get-webhook): a new `secret_rotated_at` means it rotated, and [retrieving the secret](https://cherami.to/docs/api/webhooks/get-webhook-secret) gives you the new one.                                                                                                                                      |
| `webhook_settings_uncertain`     | 503         | The settings change was not confirmed. Apply the same values again; repeating them is safe.                                                                                                                                                                                                                                                                                                           |
| `webhook_headers_uncertain`      | 503         | The header change was not confirmed. Apply the same change again; repeating it is safe.                                                                                                                                                                                                                                                                                                               |
| `webhook_headers_unavailable`    | 503         | Header names could not be read. When `error.details.applied` is true the change was made; list the headers again.                                                                                                                                                                                                                                                                                     |
| `webhook_history_unavailable`    | 503         | Delivery history could not be read. Retry later.                                                                                                                                                                                                                                                                                                                                                      |

A send the provider refused is not an error here: it returns a sent message with `status: "rejected"` and an `error_code` such as `E_RECIPIENT_SUPPRESSED`. See [sending outcomes](https://cherami.to/docs/api/sending/send-message#response-and-outcomes).

## Retry by operation [#retry-by-operation]

After a `5xx`, a timeout or a lost response, a write may have completed. Recover by operation:

| Operation                     | After an uncertain response                                                                                                                                                                                                                                            |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Read                          | Retry after a short wait.                                                                                                                                                                                                                                              |
| Create an inbox or draft      | Repeat with the original key and payload within 24 hours. Without a key, look for it in the listing before creating again. See [inbox recovery](https://cherami.to/docs/api/inboxes/create-inbox#recover-creation) and [draft recovery](https://cherami.to/docs/api/drafts/create-draft#recover-creation). |
| Send, reply or forward        | Repeat with the original key and unchanged payload within [24 hours](https://cherami.to/docs/api/sending/send-message#retry-a-send-with-an-idempotency-key). Without a key, check sent messages instead of sending again.                                                                |
| Send a draft                  | Repeat the same draft send at any time; it never sends twice.                                                                                                                                                                                                          |
| Edit inbox names or a draft   | Read the current state, then repeat the change if it's still needed.                                                                                                                                                                                                   |
| Change labels                 | Repeat the change; labels are never duplicated.                                                                                                                                                                                                                        |
| Delete                        | Repeat the request; it returns `202` or `404`.                                                                                                                                                                                                                         |
| Restore from Trash            | Repeat the request; `status: "already_live"` means the message is already back.                                                                                                                                                                                        |
| Redeem an approval phrase     | Ask your human for a new approval ([recovery](https://cherami.to/docs/guides/recovery)).                                                                                                                                                                                                 |
| Feedback                      | Repeating creates a duplicate; send again only if that's acceptable.                                                                                                                                                                                                   |
| Add a webhook                 | List webhooks, find the URL and retrieve its secret rather than adding it again.                                                                                                                                                                                       |
| Edit a webhook or its headers | Apply the same values again; they're absolute.                                                                                                                                                                                                                         |
| Rotate a webhook secret       | Read the webhook first: a new `secret_rotated_at` means the rotation happened.                                                                                                                                                                                         |
| Delete a webhook              | Repeat the request; `pending` removal finishes in the background.                                                                                                                                                                                                      |

## Pagination [#pagination]

Listings of received and sent messages, drafts, labels, conversations, a conversation's messages, Trash, webhook events and delivery attempts take `limit` (1–100, default 20) and return `next_cursor`. Send `next_cursor` back, URL-encoded, as `cursor` with the same URL, filters and order, and stop when it is null:

```http
GET /v1/inboxes/INBOX_ID/messages?limit=20&cursor=URL_ENCODED_CURSOR HTTP/1.1
Host: cherami.to
Authorization: Bearer YOUR_CREDENTIAL
```

Pages are live: in the default newest-first order, new mail appears on the first page, so start again from there to find it. [Receive and poll for mail](https://cherami.to/docs/guides/receiving) covers polling.

## Limits [#limits]

An account's allowances are in `inbox_allowance` from [List inboxes](https://cherami.to/docs/api/inboxes/list-inboxes) and in [Read sending allowance](https://cherami.to/docs/api/sending/get-outbound-quota); [Get help or request more allowance](https://cherami.to/docs/guides/support) covers getting more. These technical limits apply to every account:

| Resource                    | Limit                                                                       |
| --------------------------- | --------------------------------------------------------------------------- |
| Incoming email              | 25 MiB, including encoded attachments.                                      |
| Outgoing email              | 5 MiB, counting text, HTML and base64 attachments.                          |
| Send and draft request JSON | 8 MiB.                                                                      |
| Recipients per message      | 50 across To, Cc and Bcc.                                                   |
| Attachments per message     | 32.                                                                         |
| Subject                     | 998 UTF-8 bytes, without control characters.                                |
| Feedback request JSON       | 20 KiB.                                                                     |
| Label-update JSON           | 20 KiB for one message or a conversation; 32 KiB for a bulk update.         |
| Bulk label targets          | 1–100 message IDs per request.                                              |
| Label names                 | 1–128 UTF-8 bytes after trimming, without control characters.               |
| Labels per request array    | 32 in each add or remove array, a send's initial labels, or a filter group. |
| Other JSON requests         | 4 KiB.                                                                      |
| Signup discovery            | 10 requests per IP per hour.                                                |
| Agent approvals in Account  | 10 per IP per 15 minutes.                                                   |
| Phrase redemption           | 10 attempts per IP per 15 minutes.                                          |
| Approval phrase             | One use, within 6 hours.                                                    |
| Webhook inbox scope         | 10 inboxes per webhook; empty means every inbox.                            |
| Webhook custom headers      | 20 set and 20 removed per request; values at most 1024 characters.          |
