This page is a reference for error codes returned by the Telnyx Email API. It covers two distinct families: synchronous HTTP errors returned on the API request itself, and asynchronous delivery errors (the 30xxx taxonomy) reported later via webhooks and detail records. They are not interchangeable — a 30xxx code never appears in an HTTP response body, and an HTTP code never appears in
error_evidence. For endpoint-specific errors, see the response examples in each endpoint’s API reference.Error response format
Most errors follow the standard Telnyx v2 error shape (exceptions: batch errors use{index, code, message} — see Batch-specific errors — and some template render errors use {code, message}):
Request and message size limits
Size failures are a common source of confusion because three different ceilings apply at three different layers. They are not the same number.The edge caps are not request-body limits. They are the Edge gateway’s
request_body_cap_bytes for idempotency-keyed replay only. A keyed request over its cap is rejected at the Edge with 413 Payload Too Large — it never reaches the Email API. Unkeyed requests bypass these caps entirely. Single sends cap at 8 MB; the batch endpoint caps at 20 MB. The limits that actually reject a message body are the 1 MB body and 25 MB total enforced by the Email API, which return 422.HTTP status codes
Error code reference
Every code in this section is a synchronous error — returned in the HTTP response to your API request. Delivery failures that happen after a
202 Accepted use the separate 30xxx taxonomy.400 — Bad Request
401 — Unauthorized
403 — Forbidden
404 — Not Found
409 — Conflict
422 — Unprocessable Entity
429 — Too Many Requests
domain_graduation_limit_exceeded example (the meta fields and the ramp stage in detail are present only when known — they are omitted, never null):
10011 without consulting the ramp at all. Both are recipient-count limits, so a send must fit both: whichever remaining headroom is lower is the binding constraint, and exceeding the domain’s is what returns domain_graduation_limit_exceeded. See Rate Limits & Quotas for the full ordering and counting rules.
Do not treat every
429 as a transient rate limit. Branch on the error code, not the status. 10011 clears on its own at midnight UTC. domain_graduation_limit_exceeded clears at the domain’s midnight UTC ramp reset — honor the Retry-After header, which only this 429 sets (10011 and reputation_suspended never do). reputation_suspended does not clear by waiting — retrying it in a backoff loop will never succeed and worsens the reputation signal. Stop sending on that domain and remediate the underlying bounce/complaint rates first.Scheduled sends are never rejected with a
429 at fire time. A scheduled send bypasses admission when it is accepted and is evaluated against both the account quota and the sender-domain ramp when the scheduled worker fires. There is no HTTP request in flight then. A ramp rejection terminalizes recipients as failed with reason sender_domain_graduation_rejected; a fail-closed graduation dependency failure that exhausts its retries uses sender_domain_graduation_unavailable. Both store and publish recipient-scoped email.failed events, and normal rollup can leave the parent message completed. There is no synchronous HTTP error code for either path—detect them through recipient events or webhooks.500 — Internal Server Error
503 — Service Unavailable
error_evidence structure
Failure events carry the normalized contract as error_evidence:
The same normalized error is also published in array form as
errors[], where each entry adds a human-readable title and renames message to detail. See Error evidence on failure events.
The webhook event type does not uniquely identify the failure. An ordinary bounce, a queue expiration, an administrative bounce, and an out-of-band bounce all publish
email.bounced. Branch on error_evidence.code — for example 30001 versus 30005 — and read the recipient status for the authoritative outcome. bounce_category is an internal field: it is not part of normal recipient-scoped webhook payloads and is not written to normal recipient-scoped stored events. A legacy message-scoped fallback path may persist it in stored events, and the Events API sanitizer does not explicitly strip it. Do not build consumer logic that reads bounce_category from any public surface.Idempotency-specific errors
When using theIdempotency-Key header, idempotency is enforced at the Telnyx Edge (API gateway) before the request reaches the Email API. The same key can produce three distinct outcomes, and they must be handled differently:
Two further failure modes are specific to the idempotency layer itself:
A replay only works while the gateway holds the stored response. Requests whose body exceeds the Edge replay cap — 8 MB for single sends, 20 MB for batches — are rejected at the Edge with
413 Payload Too Large before reaching the Email API — see Request and message size limits.Batch-specific errors
Batch requests (POST /email_messages/batch, up to 1,000 messages) return 207 Multi-Status for every processed batch — including an all-success batch, where errors is empty — with per-message errors:
index (position in your messages array), code, and message:
Codes owned by other services
The Telnyx email product spans several services. A few codes documented here are returned by services other than the Email API, which means their exact status, code, and detail can change independently of this page. Verify these against the owning service’s API reference before depending on the precise shape:Error codes are not globally unique across Telnyx email services.
10008 is the clearest example: the email-domains service returns it as a 403 Forbidden for shared-domain mutation, while the Email API returns the same code as a 503 Service Unavailable when diagnostics authentication is unavailable. Always interpret a code together with both the HTTP status and the endpoint that returned it — never on the code alone.Troubleshooting
”Domain is not verified”
- Check
GET /v2/email_domainsto see the domain status. - Ensure all required DNS records — ownership and DKIM, plus MX if
inbound_enabledis true — are published and match the records returned byGET /v2/email_domains/{domain_id}/dns_records. - Call
POST /v2/email_domains/{domain_id}/verifyafter DNS propagates. - For a controlled zero-setup test, use
onboarding@<shared-domain>and send only to the account owner’s verified email address.
”sender address is not allowed”
Thefrom address must be on a verified domain you own. Shared-domain sends must use onboarding@<shared-domain> and can target only the account owner’s verified email address.
”Idempotency-Key header is invalid”
- Generate a UUID v4 (
uuidgenorcrypto.randomUUID()). - Pass it as an HTTP header:
Idempotency-Key: 8e03978e-40d5-43e8-bc93-6894a57f9326. - Do not include it in the JSON body — it’s a header only.
- One key per logical request. Reuse only for exact retries.
Sending suspended (reputation_suspended)
Your domain’s reputation band dropped to poor — usually from high bounce or complaint rates. See Deliverability and Domain Warm-up for recovery guidance.
Sender-domain ramp rejection (domain_graduation_limit_exceeded)
Your sending domain is still in warm-up and the send would have exceeded the domain’s daily recipient allowance for the current ramp stage. This is a volume cap, not a reputation or account-quota problem:
- Honor the
Retry-Afterresponse header — it holds the seconds until the domain’s midnight UTC reset. The errormetamay also carryretry_after_secondsandremaining_today(omitted when unknown). - Check whether sibling subdomains share the ramp — the allowance is per registrable organizational domain, so mail across
mail.example.comandnews.example.comdraws from one daily cap. - Spread the remaining volume: submit fewer recipients per day, or queue and resume after the UTC reset.
- If the ramp stage is not advancing as expected, see Deliverability and Domain Warm-up — a reputation warning pauses advancement without lowering the current cap.
failed events with reason sender_domain_graduation_rejected instead of a 429, a scheduled send exceeded the ramp at fire time. Reason sender_domain_graduation_unavailable means a fail-closed graduation dependency failure exhausted its scheduled-worker retries. Neither has a synchronous HTTP code; see Rate Limits & Quotas.
Choosing a retry strategy
Retry decisions belong on the error code, not the HTTP status. Two errors that share a status can need opposite handling.Never retry a
4xx other than 409 and 429 without changing the request — the outcome is deterministic. And when you do retry a send, always reuse the original Idempotency-Key so a retry that races a slow success cannot deliver the message twice.Delivery failed after a 202 Accepted
A 202 only confirms acceptance for sending. If the message never arrived, the failure is asynchronous — look at the 30xxx delivery error on the recipient’s webhook or event, not at the HTTP response. Start with error_evidence.code:
30001— permanent rejection. Remove the address.30002— temporary; Telnyx is already retrying. Do not resubmit.30004— the recipient was suppressed before any attempt.30005— retries were exhausted; the recipient status isexpired.