---
title: "Create a batch of email messages"
method: POST
path: "/v1/email/batches"
tags: ["email-messages"]
---

# Create a batch of email messages

`POST /v1/email/batches`

## Request

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

Accepts up to 100 independent email messages and queues them for delivery. All items are validated before any are queued: if one fails validation, the entire batch is rejected. Field-level validation failures and business-rule failures, such as sending from a domain that is not verified, both return `422`. An item can set `scheduled_at` to send it later, on the same terms as a single scheduled send, and one batch can mix scheduled and immediate messages. Cancel a scheduled item before it sends with [Cancel an email message](/docs/api/reference/cancel-email-message). Suppression is evaluated per recipient after acceptance, never as a synchronous error. The `202` response returns one entry per message in submission order, each with its own `id` you can use to fetch that message or match it against webhook events. Attachments are allowed per message. Each message must stay within the 20 MB estimated generated message-size cap, and the serialized JSON request body for the whole batch has a hard 20 MB cap.

## Headers

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

## Request body

- EmailMessageBatchRequest: Batch of email message send requests.
  - `messages` EmailMessageSendRequest[], required: Email message send requests, up to 100. All items are validated before any are queued. Attachments are allowed on individual messages. Each message must stay within the 20 MB estimated generated message-size cap. The serialized JSON request body for the batch has a hard 20 MB cap.
    - `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`

Batch accepted for asynchronous delivery.

- EmailMessageBatchResponse
  - `data` EmailMessageBatchItem[], required: One entry per message in the batch, in submission order.
    - `id` string, required
    - `status` 'accepted', required: Initial status of this message in the batch.
    - `category` 'marketing' | 'transactional', required: Resolved category for this batch item.
    - `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
    - `scheduled_at` string, date-time: When this item is scheduled to send, for an item created with a future send time. Absent for an item that sends immediately.

## 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)
