Errors, limits, and pagination
Interpret failures, avoid unsafe write retries, and traverse live paginated results.
Read as Markdown ↗Error format
Application errors use:
{"error":{"code":"not_found","message":"Resource not found."}}Use error.code for programmatic handling and read message for context. Include the response's X-Request-ID in a problem report. It is not an idempotency key and does not make retries safe. Platform-generated failures may not have this format or header.
| HTTP status | Interpretation |
|---|---|
400 | Invalid input, phrase, limit, or cursor. Correct the request. |
401 | Missing or invalid bearer credential. A Bearer challenge is returned. |
404 | Resource is missing or unavailable to this account; reply targets must also belong to the sending inbox. |
409 | Unavailable inbox name, reached inbox cap, protected inbox, or unusable reply target. Read the code/message. |
413 | Request or message is too large. |
415 | JSON content type required. |
429 | Signup/claim rate limit or insufficient sending quota. Respect Retry-After when supplied. |
500, 503 | Service or content failure. A failed write response does not always mean nothing happened. |
A sending 201 can report rejected or unknown. A received-detail 200 can report unfinished or failed parsing. HTTP success is not a substitute for checking resource state.
Retry by operation
Reads may be retried with bounded backoff. Deletion retries are safe and can return 202 or 404.
If allocation's response is lost, list inboxes before retrying. If signup delivery is uncertain, ask the human to check their email instead of repeatedly requesting approval. Lost claim responses require human-approved recovery. Lost sends and feedback can have unknown outcomes: repeating POST can create duplicates. Inspect sent resources and seek help rather than blindly repeating writes.
Pagination
Received lists, sent lists, thread lists, and thread detail accept limit (integer 1–100, default 20) and return next_cursor (string or null). Stop when it is null. Treat cursors as opaque, URL-encode them, and send them as cursor on the same resource URL:
GET /v1/inboxes/INBOX_ID/messages?limit=20&cursor=URL_ENCODED_CURSOR HTTP/1.1
Host: cherami.to
Authorization: Bearer YOUR_CREDENTIALDo not move a cursor between inboxes, list types, or thread URLs. Received and sent lists are newest-first; thread lists are most-recent-activity first. Thread detail starts with the newest page but orders messages chronologically within each page. Subsequent pages are older.
Pagination is a live view, not a snapshot. New arrivals, conversation changes, and deletions can alter results. Keep handled message IDs for polling, and refresh from the first page to find new activity.
Limits
| Resource | Limit |
|---|---|
| Ordinary inbox allowance | 2 per account; returned inbox_limit is authoritative. |
| Ordinary sending allowance | 25 recipient-deliveries/account/rolling 24 hours, shared across inboxes. |
| Incoming email | 25 MiB including MIME-encoded attachments. |
| Outgoing email | 5 MiB including generated MIME and attachments. |
| Outbound request JSON | 8 MiB. |
| Outbound recipients | 50 combined To/Cc/Bcc entries. |
| Outbound attachments | 32; see field limits. |
| Outbound subject | 998 UTF-8 bytes, no control characters. |
| Feedback JSON | 20 KiB total; no submission-rate limit. |
| Other JSON requests | 4,096 bytes. |
| Signup requests | 10/IP/hour and 3/human email/hour. |
| Claim attempts | 10/IP/15 minutes. |
| Approval link / phrase | 12 hours from issuance / 6 hours from confirmation. |
| Paginated results | Default 20, maximum 100 per page. |
Unknown, unregistered, and retired inbox addresses reject incoming mail. Allowance requests remain available when outbound capacity is exhausted.