Documentation
API reference

Errors — the error envelope and every code

5 min read

Every failed request returns the same shape, with a non-2xx status. There is one envelope to parse, always:

{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_field",
    "message": "Subject is required.",
    "param": "subject",
    "request_id": "req_01KVF46QACE5C361SBYTP6JE0S"
  }
}
  • type — the broad category; it determines the HTTP status.
  • code — the specific, stable, machine-readable reason. Branch on this.
  • message — a human-readable explanation. Do not parse it; it can change.
  • param — the offending field for validation errors, otherwise null.
  • request_id — also returned as the X-Request-Id header. Quote it to support.

Type → HTTP status

The type maps to the status, so you can handle whole classes at once:

  • invalid_request_error400 — malformed or invalid input.
  • authentication_error401 — missing, invalid, revoked, or expired key.
  • permission_error403 — the key lacks the scope, or the workspace is not approved to send.
  • payment_error402 — payment required, subscription canceled, or a gated feature.
  • resource_error404 — the referenced object does not exist.
  • abuse_error422 — the request is well-formed but disallowed (suppressed recipient, paused agent).
  • rate_limit_error429 — a send cap was hit; honor Retry-After.
  • api_error500 / 502 — an internal error or an upstream provider failure.

Common codes

The codes you will branch on most:

  • Auth (401): missing_authorization, invalid_api_key, revoked_api_key, expired_api_key.
  • Scope & approval (403): insufficient_scope, workspace_not_approved.
  • Validation (400): missing_field, invalid_field — check param.
  • Send-time (404/422): agent_not_found, agent_paused, agent_archived, recipient_suppressed.
  • Limits (429): hourly_limit_exceeded, daily_limit_exceeded, monthly_limit_exceeded, free_tier_exceeded.
  • Billing (402): payment_required, subscription_canceled, feature_not_enabled.
  • Conflict (409): duplicate_resource — an idempotency key or a draft already in flight.
  • Upstream (502): upstream_error — the mail provider rejected the send; safe to retry with backoff.

Handling errors well

  • Branch on code, not message. Codes are stable; messages are for humans and may be reworded.
  • Respect Retry-After. On a 429 the response carries a Retry-After in seconds (60 for an hourly cap, 3600 for a daily one). Back off rather than hammering.
  • Retry only the safe ones. upstream_error (502) and 500s are retryable with exponential backoff; pair retries with an Idempotency-Key so a retried send never duplicates.
  • Log the request_id. It is the fastest way for support to trace exactly what happened on a given call.