Error Reference

Standard error response format and common error codes.

Error response format

All errors return a JSON object with an error field:

error response
{
  "error": "Human-readable error message",
  "upgrade": "POST /v1/billing/upgrade to get a checkout URL"
}

The optional upgrade field appears when the error is due to a plan limitation that can be resolved by upgrading.

HTTP status codes

  • 200 — Success
  • 201 — Resource created
  • 400 — Bad request (invalid input, missing required fields)
  • 401 — Unauthorized (missing or invalid API key)
  • 402 — Payment required (monthly inbound limit reached — see below)
  • 403 — Forbidden (plan limit, scoped key, email not verified)
  • 404 — Resource not found
  • 409 — Conflict (duplicate resource)
  • 413 — Payload too large (file or message size limit)
  • 429 — Rate limited (daily send limit, monthly send limit, or signup rate limit)
  • 500 — Internal server error
  • 503 — Service unavailable (dependency down)

402 INBOUND_LIMIT_EXCEEDED

Returned by GET /v1/mailboxes/:id/messages/:msgId and GET /v1/attachments/:id when the requested resource belongs to a message that was received while the account was over its monthly inbound cap (Free 100 / Developer 2,000 / Growth 20,000 / Scale unlimited). The message is safely persisted — a subsequent upgrade or the monthly reset makes it visible on the list endpoints again.

402 response
{
  "error": "Monthly inbound limit reached — upgrade to unlock this message",
  "code": "INBOUND_LIMIT_EXCEEDED",
  "resource": { "type": "message", "id": "b2c3d4e5-..." },
  "upgrade": {
    "browserUrl": "https://robotomail.com/billing",
    "apiEndpoint": { "method": "POST", "path": "/v1/billing/upgrade" },
    "hint": "POST /v1/billing/upgrade for a programmatic checkout URL, or visit browserUrl to sign in and upgrade"
  },
  "resetAt": "2026-05-01T00:00:00.000Z"
}

Headers:

  • Retry-After — seconds until the monthly reset. Long-lived — agents should NOT retry until unlock.
  • X-Robotomail-Retriable: false — this error is non-retriable via backoff; upgrade or wait for the reset.
  • X-Robotomail-Reset-At — matches resetAt in the body.
  • X-Robotomail-Inbound-Quota — e.g. 101/100. Scale accounts are unlimited and never hit this error.
  • X-Robotomail-Inbound-Warning: reached.

List endpoints are unaffected. GET /v1/mailboxes/:id/messages and GET /v1/mailboxes/:id/threads always return 200; they hide over-limit messages from the body and surface the hidden count as metadata.overLimitCount. Use the list envelope to drive your upgrade flow, then re-fetch the single message after the unlock.

Inbound quota headers

Every authenticated JSON response (2xx, 4xx, 5xx) carries two headers so you can surface the account's inbound usage without a dedicated lookup. They are absent on the public /v1/signup routes, unauthenticated 401 responses, and SSE streams (which carry the same data inline at data.account.inbound_usage).

  • X-Robotomail-Inbound-Quota<count>/<limit>, e.g. 57/100. Renders as <count>/unlimited for Scale.
  • X-Robotomail-Inbound-Warningapproaching (≥ 50 %), near (≥ 80 %), reached (≥ 100 %). Absent below 50 % and on unlimited tiers.

Common error scenarios

Rate limiting (429)

response
{
  "error": "Daily send limit reached",
  "upgrade": "POST /v1/billing/upgrade to get a checkout URL"
}

For send limits, check dailySendCount on the mailbox object or sentToday / sentThisMonth via GET /v1/account to monitor daily and monthly usage.

monthly limit response
{
  "error": "Monthly send limit reached",
  "upgrade": "POST /v1/billing/upgrade to get a checkout URL"
}

Plan limits (403)

response
{
  "error": "Free plan limited to 3 mailboxes. Upgrade to create more.",
  "upgrade": "POST /v1/billing/upgrade to get a checkout URL"
}

Suppressed address (400)

response
{
  "error": "Recipient address is on the suppression list: invalid@example.com"
}

Remove the address from the suppression list if you believe it's now valid.