Skip to main content
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.
Attachments are base64-encoded in the request, so a 25 MB message occupies roughly 33 MB on the wire. The Email API measures decoded bytes.

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):
The account quota is checked before the domain ramp — a send rejected by the account quota returns 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 the Idempotency-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:
Each batch error entry has 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”

  1. Check GET /v2/email_domains to see the domain status.
  2. Ensure all required DNS records — ownership and DKIM, plus MX if inbound_enabled is true — are published and match the records returned by GET /v2/email_domains/{domain_id}/dns_records.
  3. Call POST /v2/email_domains/{domain_id}/verify after DNS propagates.
  4. 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”

The from 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 (uuidgen or crypto.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:
  1. Honor the Retry-After response header — it holds the seconds until the domain’s midnight UTC reset. The error meta may also carry retry_after_seconds and remaining_today (omitted when unknown).
  2. Check whether sibling subdomains share the ramp — the allowance is per registrable organizational domain, so mail across mail.example.com and news.example.com draws from one daily cap.
  3. Spread the remaining volume: submit fewer recipients per day, or queue and resume after the UTC reset.
  4. 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.
If you see recipient 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 is expired.