---
title: "Create or send an email message"
method: POST
path: "/email_messages"
tags: ["Email Messages"]
---

# Create or send an email message

`POST /email_messages`

## Request

- Base URL: `https://api.telnyx.com/v2`
- URL: `https://api.telnyx.com/v2/email_messages`
- Auth: HTTP bearer

Queues, schedules, or sandbox-sends an email message. The legacy `/v2/emails` POST route
is a backward-compatible alias for this operation.

`subject` is required unless `template_id` is supplied. When using `template_id`, do not
also provide `subject`, `html_body`, or `text_body`; the template is rendered with
`template_variables`.

Note: template lookup failures (not found, wrong account) return 400, not 404.

## Headers

- `Idempotency-Key` string

## Request body

- CreateEmailRequest: Recipient email addresses must be unique across `to`, `cc`, and `bcc` after case-insensitive normalization. Duplicate recipients return `400 Bad Request`.
  - `from` union, required
    - string
    - EmailAddress
      - `email` string, required
      - `name` string
  - `from_name` string: Optional display name for string `from`; overrides `from.name` when provided.
  - `to` EmailAddressInput[], required
    - union
      - string
      - EmailAddress
        - `email` string, required
        - `name` string
  - `cc` EmailAddressInput[]
    - union
      - string
      - EmailAddress
        - `email` string, required
        - `name` string
  - `bcc` EmailAddressInput[]
    - union
      - string
      - EmailAddress
        - `email` string, required
        - `name` string
  - `reply_to` union
    - string
    - EmailAddress
      - `email` string, required
      - `name` string
  - `subject` string: Required unless `template_id` is supplied. When using a template, the template's subject is rendered; if the template has no subject or renders empty, the request returns 400.
  - `html_body` string: HTML email body. Returned only by `GET /email_messages/{id}`; omitted from create and list responses.
  - `text_body` string: Plain text email body. Returned only by `GET /email_messages/{id}`; omitted from create and list responses.
  - `headers` object: Custom email headers. Write-only; not returned in responses.
  - `attachments` AttachmentRequest[]
    - `filename` string: Attachment filename. Defaults to "attachment" when omitted.
    - `content_type` string: MIME content type. Defaults to "application/octet-stream" when omitted.
    - `content` string: Attachment content, typically Base64-encoded. Defaults to empty string when omitted.
    - `disposition` string: MIME disposition (`attachment` or `inline`).
    - `content_id` string, nullable: MIME Content-ID used to reference an inline attachment.
  - `tags` string[]: Tags for categorization and filtering. Stored on the message, returned on message responses, and propagated to Email Detail Records. Usable in `filter[tags]` when listing messages.
  - `group_id` string, uuid, nullable: Optional unsubscribe-group UUID used for group-scoped suppression checks and unsubscribe handling.
  - `ignore_suppression` boolean: When true, allows delivery to recipients whose suppressions explicitly permit an override. Hard bounces, spam complaints, and invalid-address suppressions cannot be overridden. Requires the `email:override` API scope.
  - `metadata` object: Custom metadata key/value pairs. Stored on the message, returned on message responses, and propagated to Email Detail Records. Usable in `filter[metadata]` when listing messages.
  - `tracking_settings` TrackingSettings: Per-send open and click tracking overrides. Omitted properties inherit the sender domain's tracking settings.
    - `open_tracking` boolean: Whether to inject an open-tracking pixel for this message.
    - `click_tracking` boolean: Whether to rewrite links for click tracking in this message.
  - `template_id` string, uuid
  - `template_variables` object: Variables for Liquid template rendering. Non-object values may cause a 422 validation error on message creation, but are silently treated as an empty object for template rendering. When the template enables `strict_variables`, a missing required variable fails the request with 422 (single send) or a per-item `unprocessable_entity` error (batch) naming the variable; no message is persisted for the failed item.
  - `scheduled_at` string, date-time, nullable: Future ISO 8601 delivery time. Invalid or non-future timestamps are rejected. Single sends return HTTP 422; in batch sends the invalid item is reported in the 207 per-item errors while other items continue. `send_at` remains a deprecated request alias. A non-null `scheduled_at` takes precedence over `send_at`; when `scheduled_at` is omitted or null, `send_at` is used.
  - `send_at` string, date-time: Deprecated alias for `scheduled_at`.
  - `inline_css` boolean
  - `sandbox_mode` boolean: Validates and accepts the message without injecting it into the MTA or outbound Kafka path. Nothing is delivered: sandbox records are non-billable, consume no daily-send-limit quota, and feed no delivery-reputation signals. The reserved sandbox test-recipient domain is `test.telnyx.com`. In sandbox mode, these addresses produce deterministic recipient-scoped lifecycle events: - `delivered@test.telnyx.com`: queued -> sending -> sent -> delivered - `hard-bounce@test.telnyx.com`: queued -> sending -> sent -> bounced (permanent) - `soft-bounce@test.telnyx.com`: queued -> sending -> sent -> bounced (transient) - `complaint@test.telnyx.com`: queued -> sending -> sent -> complained - `suppressed@test.telnyx.com`: queued -> suppressed - `invalid@test.telnyx.com`: queued -> sending -> failed (invalid recipient) - `dkim-fail@test.telnyx.com`: queued -> sending -> failed (DKIM unavailable) - `rate-limit@test.telnyx.com`: queued -> sending -> failed (rate limit exceeded) Matching is case-insensitive for both the local part and the domain and requires the exact domain `test.telnyx.com` — subdomains and other domains do not match. Mixed sandbox sends simulate only reserved test recipients; other recipients retain ordinary sandbox behavior (accepted, no delivery attempted). Hard-bounce and complaint outcomes also use the normal automatic-suppression pipeline. Non-sandbox sends to these addresses use the normal delivery path.
  - `in_reply_to_message_id` string, uuid, nullable: Telnyx message UUID of the message this send replies to. When provided, the API sets RFC 5322 `In-Reply-To` and `References` headers on the outbound MIME so the recipient's mailbox (Gmail/Outlook) threads it correctly. The parent is looked up under the caller's account scope; a UUID belonging to another account yields a non-enumerating 404. Wire-only (Phase 1): the API sets the headers and does NOT resolve or mutate `thread_id` on the server side. Messages sent without this parameter are standalone (no threading headers injected). Cannot be combined with `forward_of_message_id` (422).
  - `reply_to_all` boolean, nullable: Indicates a reply-all intent. In Phase 1 (wire-only) this does not change the threading headers — recipient selection is customer- controlled (`to`/`cc`), and a thread is not defined by its audience. When the referenced message has no thread context, reply-all degrades to a plain reply (parent ID only in `References`). The resolution engine (separate work) will expand the ancestor chain at a later phase with no API change. Only meaningful alongside `in_reply_to_message_id`.
  - `forward_of_message_id` string, uuid, nullable: Telnyx message UUID of the message this send forwards. Forwarded messages start a NEW thread per RFC 5322 — NO `In-Reply-To` or `References` headers are set on the outbound MIME. The id is recorded in the message's metadata for EDR provenance only. The id is validated as a UUID but is NOT looked up against the message store — existence is the caller's responsibility (the forward is pure metadata; it does not affect delivery). Cannot be combined with `in_reply_to_message_id` (422).

## Response `202`

Message queued, scheduled, or sandbox-created.

- EmailMessageResponse
  - `data` EmailMessage, required
    - `record_type` 'email_message', required
    - `id` string, uuid, required
    - `status` 'queued' | 'scheduled' | 'cancelled' | 'sandbox' | 'sending' | 'sent' | 'failed' | 'deferred' | 'delivered' | 'bounced' | 'complained' | 'rejected' | 'opened' | 'clicked' | 'unsubscribed', required: Current status of an email message. Lifecycle statuses (queued, scheduled, etc.) are set on creation. Delivery statuses (delivered, bounced, etc.) are updated by delivery event consumers.
    - `from` EmailAddress, required
      - `email` string, required
      - `name` string
    - `to` EmailAddress[], required
      - `email` string, required
      - `name` string
    - `cc` EmailAddress[], required
      - `email` string, required
      - `name` string
    - `bcc` EmailAddress[], required
      - `email` string, required
      - `name` string
    - `reply_to` string, nullable, required
    - `subject` string, required
    - `template_id` string, uuid, nullable, required
    - `template_variables` object, required
    - `tags` string[], required: Customer-supplied tags stored with the message.
    - `metadata` object, required: Customer-supplied metadata stored with the message.
    - `attachments` AttachmentResponse[], required
      - `url` string, uri, nullable, required: Telnyx-hosted public URL for the attachment content.
      - `sha256` string, nullable, required: SHA-256 hex digest of the attachment content.
      - `size_bytes` integer, nullable, required: Attachment size in bytes.
      - `filename` string, required
      - `content_type` string, required
      - `disposition` string, required: MIME disposition (e.g. `attachment` or `inline`). Runtime passes through the stored value without enforcing an enum.
      - `content_id` string, nullable, required: MIME Content-ID for inline references.
    - `events` EmbeddedMessageEvent[], required
      - `type` 'queued' | 'deferred' | 'scheduled' | 'cancelled' | 'sandbox' | 'sending' | 'sent' | 'failed' | 'delivered' | 'bounced' | 'complained' | 'suppressed' | 'rejected' | 'opened' | 'clicked' | 'unsubscribed' | 'daily_limit_exceeded' | 'scan_deferred' | 'quarantined' | 'quarantine_released' | 'quarantine_release_dispatched' | 'quarantine_rejected' | 'quarantine_expired' | 'gw_reject' | 'injection_timeout' | 'expired', required: Bare stored event names returned by message history. In addition to the normal send and delivery lifecycle, polling can expose suppression, scan, and quarantine lifecycle rows. Sharp canonical names gw_reject, injection_timeout, and expired distinguish gateway rejection, ambiguous injection timeout, and MTA expiration. The failed and bounced names remain valid for system/admin failures and hard bounces respectively. Existing stored rows retain their original names.
      - `occurred_at` string, date-time, required
      - `payload` object
    - `created_at` string, date-time, required
    - `scheduled_at` string, date-time: Present when a scheduled_at value was stored. Persists even after the scheduled send has been processed or cancelled.
    - `inline_css` boolean: Present when true in the immediate create response. Not persisted; absent on subsequent GET requests.
    - `sandbox` boolean: Present when sandbox mode was used.
    - `recipient_statuses` object: Per-status recipient counts for the message. Present only for outbound messages with recipient rows. Keys are recipient statuses, values are counts. Example: `{"delivered": 998, "bounced": 2}`.
    - `suppressed` SuppressedRecipient[]: Recipients excluded from delivery by suppression checks, with reasons. On batch items, present when that item had suppressed recipients; all other recipients of the item still receive the message. For single sends this information appears at the top level of the response instead (see EmailMessageResponse.suppressed).
      - `to` string, email, required: Suppressed recipient email address.
      - `reason` string, required: Suppression reason returned by the recipient suppression service.
      - `scope` string, required: Scope at which the suppression applies.
      - `override_allowed` boolean, required: Whether an authorized send may override this suppression.
  - `suppressed` SuppressedRecipient[]: Recipients removed by suppression checks when at least one recipient remains and the message is accepted.
    - `to` string, email, required: Suppressed recipient email address.
    - `reason` string, required: Suppression reason returned by the recipient suppression service.
    - `scope` string, required: Scope at which the suppression applies.
    - `override_allowed` boolean, required: Whether an authorized send may override this suppression.

## Other responses

- `400`: Bad Request / Validation Failed (10015). Invalid, duplicate, empty, malformed, or overlong Idempotency-Key headers are rejected by Edge with HTTP 400 and error code 10015.
- `401`: Not authorized (10006).
- `403`: Forbidden (10007), such as domain not verified, suspended, degraded, or missing DKIM.
- `404`: Resource not found (10001).
- `409`: A request with the same Idempotency-Key is still being processed (10036). Retry later with the same key and request.
- `413`: Request body exceeds the 8,000,000-byte limit for this endpoint.
- `422`: Validation failed (including an invalid or non-future `scheduled_at`/`send_at`), send-time template rendering failed (including a strict-variable failure naming the missing required variable), or all recipients suppressed (code `recipient_suppressed`, includes top-level `suppressed` array). Reusing an Idempotency-Key with a different request also returns 422 (10027).
- `429`: Send rejected before message creation because the account daily recipient quota was exhausted (10011), the sender-domain graduation ceiling was exceeded (domain_graduation_limit_exceeded), or domain reputation is poor (reputation_suspended).
- `503`: Service unavailable (10016), including an unavailable upstream dependency or unavailable Edge idempotency protection for a keyed request.

## Changes

- **2026-10-15** `a72569d69430`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/team-telnyx/apis/telnyx-api/changes/email_messages/post.md)

---

[API](https://skmtc.dev/team-telnyx/apis/telnyx-api.md) - [All operations](https://skmtc.dev/team-telnyx/apis/telnyx-api/llms.txt) - [OpenAPI document](https://skmtc.dev/team-telnyx/apis/telnyx-api/revisions/0f17b8167873?raw)
