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, otherwisenull.request_id— also returned as theX-Request-Idheader. Quote it to support.
Type → HTTP status
The type maps to the status, so you can handle whole classes at once:
invalid_request_error→ 400 — malformed or invalid input.authentication_error→ 401 — missing, invalid, revoked, or expired key.permission_error→ 403 — the key lacks the scope, or the workspace is not approved to send.payment_error→ 402 — payment required, subscription canceled, or a gated feature.resource_error→ 404 — the referenced object does not exist.abuse_error→ 422 — the request is well-formed but disallowed (suppressed recipient, paused agent).rate_limit_error→ 429 — a send cap was hit; honorRetry-After.api_error→ 500 / 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— checkparam. - 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, notmessage. Codes are stable; messages are for humans and may be reworded. - Respect
Retry-After. On a 429 the response carries aRetry-Afterin 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 anIdempotency-Keyso 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.