Errors — the error envelope and every code
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 usually 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.resets_atandretry_after_seconds— on every 429: when the request can succeed (UTC) and how many seconds away that is. Both arenullwhen waiting will not help, for a cap that only an upgrade or a deletion lifts.
Type → HTTP status
The type usually maps to the status, so you can handle whole classes at once:
invalid_request_error→ 400 — malformed or invalid input. Some of its codes answer another status, such asduplicate_resourceandagent_name_conflict(409),body_too_large(413) andunknown_test_address(422).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 limit was hit; retry at the body’sresets_at.api_error→ 500 / 502 / 503 — an internal error, an upstream provider failure, or a service briefly unavailable.
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(a send or a draft that would create an agent needsmanage),workspace_not_approved. - Connected apps (403):
connected_app_not_allowed— an app connected by signing in asked for something no such app may do (see Connected apps). - Test mode (403):
live_mode_required— test mode asked for something only live mode may do: trash or delete a thread that holds live mail, change, send or delete a draft a live key made, reschedule or cancel a live scheduled send, or run the forwarding test, which sends a real email. A test key, or a dashboard session sendingX-Mails-Mode: test, also gets it when it revokes a live key, or changes, pauses or archives an agent that has a live key tied to it, live mail (sent, scheduled or received) or a draft a live key made; do it with a live key or from the dashboard. - Validation (400/422):
missing_field,invalid_field,invalid_param_value(a value too long, too short, malformed or not one of those allowed) — checkparam. - Send-time (404/422):
agent_not_found,agent_paused,agent_archived,recipient_suppressed,recipient_not_allowed(a recipient outside the agent’sallowlist_domainsor on itsblocklist_domains), andunknown_test_address(422) for an address attest.mails.aiother than the test address. - 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. - Agent name (409):
agent_name_conflict— two live agents share the name a request used; use the agent’sagt_id. - Upstream (502/503):
upstream_error(usually 502) — the email service did not confirm it took the mail, so it is recorded as not sent, and the message names a request id to quote to support;upstream_unavailable(503) — it is not taking more mail right now and nothing was sent, so send it again afterRetry-After.
duplicate_resource also answers a change that came too late: editing or deleting a draft, or rescheduling or canceling a scheduled message, after it started sending, and deleting a thread for good while one of its messages is being sent. Read the resource again before you retry.
An agent named by its name resolves to the calling key’s own agent first, then to a live agent over an archived one. Only the agent’s own routes (reading, changing or archiving it, its forwarding test, its inbox), an agent_id filter, reputation and POST /v1/test/inbound answer agent_name_conflict when two agents still match; a send keeps using the oldest of them. A key tied to one agent never gets that 409, which would name other agents to it. To such a key, a shared name that is not its own agent’s answers as any other agent does: 404 from the agent’s own routes, and 403 from a filter, reputation and POST /v1/test/inbound.
Handling errors well
- Branch on
code, notmessage. Codes are stable; messages are for humans and may be reworded. - Retry a 429 at
resets_at. The body says when the request can succeed. ARetry-Afterheader is sent only when that is 60 seconds or less away; an hourly, daily or monthly cap usually means a longer wait and sends none. Back off rather than hammering. - Retry only the safe ones.
upstream_unavailable(503, after itsRetry-After) 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.
Every code
The code field takes one of these 61 values, the list in the API’s OpenAPI document: invalid_field, missing_field, missing_param, invalid_param_value, method_not_allowed, invalid_idempotency_key, duplicate_resource, slug_invalid, slug_reserved, slug_taken, agent_name_conflict, body_too_large, missing_authorization, missing_api_key, invalid_api_key, expired_api_key, revoked_api_key, invalid_access_token, email_not_verified, insufficient_scope, connected_app_not_allowed, sso_required, workspace_mismatch, agent_not_owned, rate_limit_exceeded, daily_limit_exceeded, hourly_limit_exceeded, monthly_limit_exceeded, agent_not_found, agent_paused, agent_archived, resource_not_found, recipient_suppressed, recipient_not_allowed, domain_not_verified, payment_required, payment_method_failed, subscription_canceled, billing_state_inconsistent, free_tier_exceeded, plan_limit_exceeded, cold_email_prohibited, agent_suspended, new_workspace_fanout_exceeded, resource_cap_exceeded, workspace_cap_exceeded, feature_not_enabled, test_mode_required, live_mode_required, unknown_test_address, workspace_not_approved, spam_pattern_detected, classifier_rejected, high_injection_score, complaint_threshold_exceeded, bounce_threshold_exceeded, internal_error, internal_server_error, upstream_error, upstream_unavailable and service_unavailable.
Was this page helpful?