# 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-Warning` — `approaching` (≥ 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](https://robotomail.com/docs/api/suppressions) if you believe it's now valid.


---

Previous: [Suppressions](https://robotomail.com/docs/api/suppressions.md) | Next: [Send your first email](https://robotomail.com/docs/guides/send-first-email.md)
