---
title: "Create an email message"
method: POST
path: "/v1/email/messages"
tags: ["email-messages"]
---

# Create an email message

`POST /v1/email/messages`

## Request

- Base URL: `https://us1.platform.bird.com`
- URL: `https://us1.platform.bird.com/v1/email/messages`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `bird_session`

Sends an email to the recipients you list explicitly in `to`/`cc`/`bcc`. Use it for
transactional sends (receipts, password resets, alerts) and for marketing sends where
you have the recipient addresses on hand. To submit many independent messages in one
request, use [Create a batch of email messages](/docs/api/reference/create-email-message-batch)
instead. The `category` field controls suppression policy independently of content:
set it to `marketing` when sending marketing content.

The `202` response means the message is safely accepted and awaiting delivery.
Fetch it by `id` or subscribe to webhook events to follow delivery. The
request never half-succeeds: an unverified sender domain or any field-level
validation failure rejects it immediately with a `422` naming the reason.
Suppression is evaluated per recipient after acceptance, so a suppressed recipient
appears as `rejected` on the message's recipient list rather than as a synchronous
error. New workspaces can send from the shared onboarding domain before verifying
their own. The [quickstart](/docs/get-started/send-your-first-email) covers its
recipient and volume limits.

## Headers

- `Idempotency-Key` string
- `X-Workspace-Id` string
- `X-Organization-Id` string

## Request body

- EmailMessageSendRequest
  - `from` union, required: A sender or recipient address. Accepts a plain email string (`jane@acme.com`), an RFC 5322 mailbox string with an embedded display name (`Jane Doe <jane@acme.com>`), or an object carrying the address and an optional display name. All forms can be mixed freely within one request. Responses always return the object form.
    - string: Email address, optionally in RFC 5322 mailbox form with an embedded display name.
    - EmailAddress: An email address with an optional display name.
      - `email` string, email, required: Email address.
      - `name` string: Display name shown alongside the address in mail clients.
  - `to` EmailAddressInput[], required: Primary recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name.
    - union: A sender or recipient address. Accepts a plain email string (`jane@acme.com`), an RFC 5322 mailbox string with an embedded display name (`Jane Doe <jane@acme.com>`), or an object carrying the address and an optional display name. All forms can be mixed freely within one request. Responses always return the object form.
      - string: Email address, optionally in RFC 5322 mailbox form with an embedded display name.
      - EmailAddress: An email address with an optional display name.
        - `email` string, email, required: Email address.
        - `name` string: Display name shown alongside the address in mail clients.
  - `cc` EmailAddressInput[]: CC recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name.
    - union: A sender or recipient address. Accepts a plain email string (`jane@acme.com`), an RFC 5322 mailbox string with an embedded display name (`Jane Doe <jane@acme.com>`), or an object carrying the address and an optional display name. All forms can be mixed freely within one request. Responses always return the object form.
      - string: Email address, optionally in RFC 5322 mailbox form with an embedded display name.
      - EmailAddress: An email address with an optional display name.
        - `email` string, email, required: Email address.
        - `name` string: Display name shown alongside the address in mail clients.
  - `bcc` EmailAddressInput[]: BCC recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@acme.com>`), or an object with an optional display name.
    - union: A sender or recipient address. Accepts a plain email string (`jane@acme.com`), an RFC 5322 mailbox string with an embedded display name (`Jane Doe <jane@acme.com>`), or an object carrying the address and an optional display name. All forms can be mixed freely within one request. Responses always return the object form.
      - string: Email address, optionally in RFC 5322 mailbox form with an embedded display name.
      - EmailAddress: An email address with an optional display name.
        - `email` string, email, required: Email address.
        - `name` string: Display name shown alongside the address in mail clients.
  - `subject` string: Message subject line. Required for inline sends. Omit it when sending a `template` (the template supplies the subject).
  - `html` string: HTML body. At least one of html or text must be provided.
  - `text` string: Plain-text body. At least one of html or text must be provided.
  - `reply_to` EmailAddressInput[]: Reply-To addresses, each a plain email string, an RFC 5322 mailbox string, or an object with an optional display name. RFC 5322 allows multiple. Every recipient reply hits all listed addresses, so 1-2 is typical. The 25 cap exists to prevent header sizes that some receiving mail servers reject.
    - union: A sender or recipient address. Accepts a plain email string (`jane@acme.com`), an RFC 5322 mailbox string with an embedded display name (`Jane Doe <jane@acme.com>`), or an object carrying the address and an optional display name. All forms can be mixed freely within one request. Responses always return the object form.
      - string: Email address, optionally in RFC 5322 mailbox form with an embedded display name.
      - EmailAddress: An email address with an optional display name.
        - `email` string, email, required: Email address.
        - `name` string: Display name shown alongside the address in mail clients.
  - `headers` object: Custom email headers, such as `References`, `In-Reply-To`, and `X-*` headers. Reserved headers and values containing carriage returns or line feeds return `422`. Set addressing and subject through their dedicated fields. Transactional sends accept `List-Unsubscribe` and `List-Unsubscribe-Post`. Marketing sends use Bird-managed unsubscribe by default.
  - `tags` Tag[]: Structured `{name, value}` labels for **filtering and analytics**. Tags become first-class query dimensions: - Filter the list endpoint by tag name. - Slice analytics rollups by tag. - Surface in webhook payloads. Cap: 20 tags per send. Use tags for low-cardinality dimensions (`category`, `experiment_variant`, `template_id`). For arbitrary structured context that you do not need as a filter dimension, use `metadata` instead.
    - `name` string, required: Tag name. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 32 characters.
    - `value` string, required: Tag value. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 64 characters.
  - `metadata` object: Arbitrary JSON object returned on API reads and included in webhook payloads. You can query its paths in analytics, such as `metadata.order_id`, but it is not a dashboard filter. The serialized object is limited to 2 KB. Use metadata for per-send context such as order IDs, customer references, and structured event data. For low-cardinality filterable labels, use `tags` instead.
  - `parameters` object: Parameter values used to personalize inline content, shared across all recipients of this send. Tokens such as `{{ animal }}` are replaced with matching values; missing values render empty. Include this object, even as `{}`, to use Liquid, or omit it to leave tokens unchanged. Use single-word names other than `bird`. Cap: 16 KB serialized. For a stored template, use `template.parameters` instead. See [inline personalization](https://bird.com/docs/guides/email/sending-email#content) for validation and URL encoding examples.
  - `template` union: A reference to the template to send. Identify the template by its `id` or its `slug`, supplying exactly one of the two, and give the values for its caller parameters in `parameters`.
    - object
      - `id` string, required
      - `slug` string: A template's slug: what you send it by, for example `welcome-email`. Email and SMS slugs stay fixed after creation. WhatsApp slugs can change only before the first submission. A slug can contain lowercase letters, numbers, hyphens, and underscores, has to start and end with a letter or a number, and can be up to 63 characters long.
      - `language` string: A language tag in BCP-47 form, for example `en` or `pt-BR`.
      - `parameters` object: Values for caller parameters, keyed by name. A parameter name is a single word. Caller parameters have `system` set to false or absent in the template's `variables` list. Supply each required caller parameter used by the resolved send language; omitting one returns `422`. A version's list covers all its languages, and values unused by the resolved language are ignored. The `bird` namespace is reserved for values filled by Bird, so a send that sets it is rejected. `parameters` is capped at 16 KB once serialized.
    - object
      - `id` string
      - `slug` string, required: A template's slug: what you send it by, for example `welcome-email`. Email and SMS slugs stay fixed after creation. WhatsApp slugs can change only before the first submission. A slug can contain lowercase letters, numbers, hyphens, and underscores, has to start and end with a letter or a number, and can be up to 63 characters long.
      - `language` string: A language tag in BCP-47 form, for example `en` or `pt-BR`.
      - `parameters` object: Values for caller parameters, keyed by name. A parameter name is a single word. Caller parameters have `system` set to false or absent in the template's `variables` list. Supply each required caller parameter used by the resolved send language; omitting one returns `422`. A version's list covers all its languages, and values unused by the resolved language are ignored. The `bird` namespace is reserved for values filled by Bird, so a send that sets it is rejected. `parameters` is capped at 16 KB once serialized.
  - `track_opens` boolean: Whether to track open events for this message.
  - `track_clicks` boolean: Whether to track click events for this message.
  - `ip_pool_id` string: ID of the IP pool to send from (`ipp_` prefix), or `ipp_shared` to route through the shared pool explicitly. Omit to use your organization's default pool. An unknown pool, or a pool with no dedicated IPs available to send from, is rejected with a `422`.
  - `category` 'marketing' | 'transactional': Content classification, which controls suppression policy: - `marketing`: Blocks on all suppression reasons. - `transactional`: Allows delivery through complaint and unsubscribe suppressions, for receipts, password resets, and similar operational mail.
  - `attachments` EmailAttachment[]: Files to attach, up to 20 per message. A message can be at most 20 MB once it has been generated, and we refuse a send that would go over. That figure covers the HTML body, the text body and every attachment and inline image, all measured after base64 encoding, which adds roughly a third. So 15 MB of raw files already accounts for most of the budget, and the body competes for the same space. A batch send is held to the same 20 MB per message, and the whole request body is capped at 20 MB as well.
    - `filename` string, required: The name the recipient sees on the attachment.
    - `content` string, byte, required: Base64-encoded file bytes. The encoded value and MIME wrapping count toward the 20 MB message limit.
    - `content_type` string: The file's MIME type. If omitted, the API infers it from the extension in `filename`. The API rejects executable and script types based on this value.
    - `content_id` string: An RFC 2392 Content-ID for an inline file. Reference it from the HTML body with `<img src="cid:{content_id}"/>`. Omit it to send a downloadable attachment.
  - `scheduled_at` string, date-time: Schedule the message to send at a future time instead of immediately. Must be at least 30 seconds and at most 30 days ahead. Outside that range the request is rejected with `422`. The message returns with status `accepted` and shows as `scheduled` on reads until it sends. Cancel it before then with the message cancel endpoint. Scheduled sends count against your plan's monthly scheduled-email allowance. Exceeding it is rejected with a `422`. For a stored template, the published version, language and parameter values are pinned when we accept the request, so a later publication does not change what sends. If the template is deleted before the message is due, the message is rejected with `generation_failure`. We also check sender eligibility and send-volume allowance when the message is due. Batch items take this field too, so one batch can mix scheduled and immediate messages.

## Response `202`

Message accepted for asynchronous delivery.

- EmailMessage: An email message, including a recipient's copy of a broadcast. `broadcast_id` identifies the broadcast that sent the message and is absent for other sends. A broadcast records one message per recipient; these copies share the same `broadcast_id`.
  - `id` string, required
  - `from` EmailAddress, required: An email address with an optional display name.
    - `email` string, email, required: Email address.
    - `name` string: Display name shown alongside the address in mail clients.
  - `to` EmailAddress[], required: Primary recipients. Length is the recipient count. Use the broadcasts endpoint for audience-targeted sends. Each entry's `name` is present when a display name was provided on the send.
    - `email` string, email, required: Email address.
    - `name` string: Display name shown alongside the address in mail clients.
  - `cc` EmailAddress[]: CC recipients.
    - `email` string, email, required: Email address.
    - `name` string: Display name shown alongside the address in mail clients.
  - `bcc` EmailAddress[]: BCC recipients.
    - `email` string, email, required: Email address.
    - `name` string: Display name shown alongside the address in mail clients.
  - `subject` string, required: The subject line as delivered. For a send that used a template, the stored subject is the template's, so this reports it with the send's `parameters` substituted in, which is what the recipient saw.
  - `category` 'marketing' | 'transactional', required: Content classification, which controls suppression policy: - `marketing`: Blocks on all suppression reasons. - `transactional`: Allows delivery through complaint and unsubscribe suppressions, for receipts, password resets, and similar operational mail.
  - `reply_to` EmailAddress[], nullable: Reply-To addresses, if set on the send. Empty/null when no Reply-To was provided.
    - `email` string, email, required: Email address.
    - `name` string: Display name shown alongside the address in mail clients.
  - `status` 'scheduled' | 'accepted' | 'processed' | 'deferred' | 'delivered' | 'partial_failure' | 'bounced' | 'complained' | 'rejected' | 'canceled', required: Aggregate delivery status of an email, derived from its recipients' states. In flight: - `scheduled`: The message is queued to send at a future time and has not been dispatched yet. - `accepted`: The initial status of an immediate send. The message is queued for its recipients. - `processed`: Delivery is underway, so at least one recipient's message is on its way out and none has failed. - `deferred`: At least one recipient's mailbox provider asked for a retry, and delivery attempts continue. Final: - `delivered`: Every recipient's mail server accepted the message. - `bounced`: Every recipient permanently failed (bounced or was rejected). - `rejected`: Every recipient was rejected before a delivery attempt (for example, all recipients were suppressed). - `partial_failure`: Some recipients permanently failed while others were delivered or are still in flight. - `canceled`: A scheduled message was canceled before it was sent. `complained` takes precedence over every other status: at least one recipient reported the message as spam, regardless of what happened to the rest.
  - `accepted_count` integer, required: How many recipients are in the `accepted` state, meaning we have the message and are getting ready to deliver it.
  - `processed_count` integer, required: How many recipients the message has been prepared for and queued for delivery.
  - `delivered_count` integer, required: How many recipients' messages were accepted by their mail server.
  - `bounced_count` integer, required: Number of recipients that resulted in a permanent delivery failure.
  - `complained_count` integer, required: Number of recipients that reported spam.
  - `deferred_count` integer, required: Number of recipients in transient delivery deferral. Their mail server asked for a retry, and delivery attempts continue.
  - `rejected_count` integer, required: Number of recipients rejected before delivery. Read the per-recipient `rejection_reason` field on `GET /v1/email/messages/{message_id}/recipients` for the specific cause.
  - `processing_latency_ms` integer, nullable: Time between the send being accepted and the message being prepared for delivery, in milliseconds, for the fastest recipient. Null until the first recipient reaches `processed`.
  - `delivery_latency_ms` integer, nullable: Time between the message being processed and the receiving mail server accepting it, in milliseconds, for the fastest delivered recipient. Null until the first recipient is delivered.
  - `total_latency_ms` integer, nullable: End-to-end accept → delivered time for the fastest delivered recipient, in milliseconds. Null until the first recipient is delivered.
  - `open_count` integer, required: Total open events across all recipients.
  - `click_count` integer, required: Total click events across all recipients.
  - `requested_language` string: A language tag in BCP-47 form, for example `en` or `pt-BR`.
  - `resolved_language` string: A language tag in BCP-47 form, for example `en` or `pt-BR`.
  - `template_id` string
  - `template_version_id` string
  - `broadcast_id` string
  - `tags` Tag[]: Labels on this message, each one a `name` and a `value`, that you can filter and search messages by. Use tags for anything you want to find messages by later, and `metadata` for data you only want handed back to you.
    - `name` string, required: Tag name. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 32 characters.
    - `value` string, required: Tag value. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 64 characters.
  - `metadata` object: Any JSON you kept on the message. We store it and hand it back in webhook payloads, and that is all it does. If you want to search or filter by it, use `tags` instead.
  - `parameters` object, nullable: The substitution values this send supplied, whether inline or from a template, or null if none were supplied. They are the values applied to `subject` and to the bodies the content endpoint returns, kept so you can see what produced the delivered copy and not only the result.
  - `attachments` EmailAttachmentRef[]: Attachment metadata for the send. Empty when no attachments were included. Raw content is not echoed. When content storage is enabled, download an attachment by its `id` via the message's attachment endpoint.
    - `id` string
    - `filename` string, required: Filename as shown to the recipient.
    - `content_type` string: Resolved MIME type at send time.
    - `size` integer, required: Decoded size in bytes.
    - `inline` boolean: True when the attachment was sent inline via a `content_id` reference in the HTML body, false for regular file attachments.
    - `content_id` string, nullable: The Content-ID set at send time, when the attachment was inline.
  - `track_opens` boolean, required: Whether open tracking is enabled for this send.
  - `track_clicks` boolean, required: Whether click tracking is enabled for this send.
  - `created_at` string, date-time, required: When the send request was accepted.
  - `thread_id` string, nullable: Thread this message belongs to, or null when the message is not part of one.
  - `in_reply_to_message_id` string
  - `delivered_at` string, date-time, nullable: When all recipients reached a terminal delivered state, or null if not yet fully delivered.
  - `scheduled_at` string, date-time: When this message is scheduled to send, for a send created with a future send time. Absent for an immediate send. Stays set after the scheduled send fires.

## Other responses

- `400`: Bad request
- `401`: Authentication required
- `402`: Insufficient balance
- `403`: Insufficient permissions
- `404`: Resource not found
- `413`: Request body or message size exceeds the allowed limit
- `422`: The request has invalid field values, violates a business rule, or carries a query parameter the endpoint does not declare. Field validation errors use `type: validation_error` and include the affected fields in `details`. Business-rule errors identify the failed rule in `type`.
- `429`: Rate limit exceeded
- `500`: Internal server error
- `503`: The service is temporarily unavailable. If `Retry-After` is present, wait for that delay before retrying; otherwise, retry with exponential backoff. Reuse the same idempotency key and request when retrying a mutation.

---

[API](https://skmtc.dev/messagebird/apis/bird-api.md) · [All operations](https://skmtc.dev/messagebird/apis/bird-api/llms.txt) · [OpenAPI document](https://skmtc.dev/messagebird/apis/bird-api/revisions/6dfce1179799?raw)
