<Intro>

<EndpointHeader />

<Description>

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 `domain_not_verified`) both return `422`. 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` to fetch or correlate webhook events against. 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.

</Description>

</Intro>

<Payload kind="request" array>

<Field name="from" type="string or object" required>

<Description>

Sender address, as a plain email string, an RFC 5322 mailbox string (`Jane <jane@example.com>`), or an object with an optional display name. Must be from a verified domain in this workspace.

</Description>

<FieldChildren kind="request">

<Field name="email" type="string" prefix="from." required>

<Description>

Email address.

</Description>

</Field>

<Field name="name" type="string" prefix="from.">

<Description>

Display name shown alongside the address in mail clients.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="to" type="array of string or object" required>

<Description>

Primary recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@example.com>`), or an object with an optional display name.

</Description>

<FieldChildren kind="request">

<Field name="email" type="string" prefix="to." required>

<Description>

Email address.

</Description>

</Field>

<Field name="name" type="string" prefix="to.">

<Description>

Display name shown alongside the address in mail clients.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="cc" type="array of string or object">

<Description>

CC recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@example.com>`), or an object with an optional display name.

</Description>

<FieldChildren kind="request">

<Field name="email" type="string" prefix="cc." required>

<Description>

Email address.

</Description>

</Field>

<Field name="name" type="string" prefix="cc.">

<Description>

Display name shown alongside the address in mail clients.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="bcc" type="array of string or object">

<Description>

BCC recipients. Each entry is a plain email string, an RFC 5322 mailbox string (`Jane <jane@example.com>`), or an object with an optional display name.

</Description>

<FieldChildren kind="request">

<Field name="email" type="string" prefix="bcc." required>

<Description>

Email address.

</Description>

</Field>

<Field name="name" type="string" prefix="bcc.">

<Description>

Display name shown alongside the address in mail clients.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="subject" type="string">

<Description>

Message subject line. Required for inline sends; omit it when sending a `template` (the template supplies the subject).

</Description>

</Field>

<Field name="html" type="string">

<Description>

HTML body. At least one of html or text must be provided.

</Description>

</Field>

<Field name="text" type="string">

<Description>

Plain-text body. At least one of html or text must be provided.

</Description>

</Field>

<Field name="reply_to" type="array of string or object">

<Description>

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 runaway header sizes that some MTAs reject.

</Description>

<FieldChildren kind="request">

<Field name="email" type="string" prefix="reply_to." required>

<Description>

Email address.

</Description>

</Field>

<Field name="name" type="string" prefix="reply_to.">

<Description>

Display name shown alongside the address in mail clients.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="headers" type="object">

<Description>

Custom email headers as key-value pairs (for example `References`, `In-Reply-To`, or your own `X-*` headers). Reserved headers are rejected with a `422`: set the message's addressing and subject through the dedicated fields (`from`, `to`, `cc`, `bcc`, `reply_to`, `subject`) rather than here, and headers the platform generates for you — `Content-Type`, `Content-Transfer-Encoding`, `DKIM-Signature`, `Received`, and `Return-Path` — cannot be overridden. `List-Unsubscribe` and `List-Unsubscribe-Post` are honored as-is on `transactional` sends; on `marketing` sends the platform sets a compliant unsubscribe header for you, so supplying them there is rejected with a `422`. Header values may not contain carriage-return or line-feed characters.

</Description>

</Field>

<Field name="tags" type="array of object">

<Description>

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, and 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.

</Description>

<FieldChildren kind="request">

<Field name="name" type="string" prefix="tags." required>

<Description>

Tag name. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 32 characters.

</Description>

</Field>

<Field name="value" type="string" prefix="tags." required>

<Description>

Tag value. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 64 characters.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="metadata" type="object">

<Description>

Arbitrary JSON object **stored, returned on API reads, and echoed in webhook payloads**. Path-queryable in analytics (e.g. filter on `metadata.order_id`) but not surfaced as a first-class dashboard filter dimension. Cap: 2 KB serialized. Use metadata for per-send context like internal IDs, foreign keys, and structured payloads you want round-tripped through events. For low-cardinality filterable labels, use `tags` instead.

</Description>

</Field>

<Field name="parameters" type="object">

<Description>

Template variables used to personalize inline content. Tokens in the subject and body (e.g. `{{ first_name }}`) are replaced with these values at send time. Shared across all recipients of this send. A token with no matching key renders empty. Cap: 16 KB serialized. When sending a stored `template`, put the values in `template.parameters` instead.

</Description>

</Field>

<Field name="template" type="object">

<Description>

Send a stored template instead of inline content. When set, omit `subject`/`html`/`text` — the template supplies them; personalize with `template.parameters`.

</Description>

</Field>

<Field name="track_opens" type="boolean">

<Description>

Whether to track open events for this message.

</Description>

</Field>

<Field name="track_clicks" type="boolean">

<Description>

Whether to track click events for this message.

</Description>

</Field>

<Field name="ip_pool_id" type="string">

<Description>

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`.

</Description>

</Field>

<Field name="category" type="string">

<Description>

Content classification — independent of which endpoint you use. Controls suppression policy: `marketing` blocks on all suppression reasons (use for marketing content); `transactional` allows delivery through complaint and unsubscribe suppressions (use for receipts, password resets, and similar operational messages). Default: marketing.

</Description>

<Description>

Possible values: `marketing`, `transactional`

</Description>

</Field>

<Field name="in_reply_to_message_id" type="string">

<Description>

Preview feature — threaded replies. Currently unavailable; supplying this field returns `422 UnsupportedEmailFeature`. When generally available, sets In-Reply-To and References headers automatically.

</Description>

</Field>

<Field name="attachments" type="array of object">

<Description>

File attachments. Bird rejects sends whose estimated generated message size exceeds 20 MB. The estimate is the HTML and text body plus all attachments and inline images measured after base64 encoding. Keep total raw attachment content at or below 15 MB for reliable headroom. In batch sends, this per-message cap still applies and the serialized JSON request body for the whole batch has a hard 20 MB cap. See the EmailAttachment schema for the full field contract.

</Description>

<FieldChildren kind="request">

<Field name="filename" type="string" prefix="attachments." required>

<Description>

Filename shown to the recipient. Required.

</Description>

</Field>

<Field name="content" type="string" prefix="attachments." required>

<Description>

Base64-encoded attachment bytes. Required. Counts toward the 20 MB estimated generated message-size cap after encoding and MIME wrapping.

</Description>

</Field>

<Field name="path" type="string" prefix="attachments.">

<Description>

Preview feature — provide a URL and Bird fetches the attachment for you. Currently unavailable. Use `content` instead. The schema currently requires `content`, so a request with only `path` is rejected with 422 for missing `content`; a request supplying both `content` and `path` is rejected with 422 `UnsupportedEmailFeature` until this preview ships. When generally available: HTTPS-only, single redirect followed and re-validated, private IP ranges blocked, request timeout enforced, fetched content counts toward the 20 MB estimated generated message-size cap after encoding and MIME wrapping.

</Description>

</Field>

<Field name="content_type" type="string" prefix="attachments.">

<Description>

MIME type. Inferred from `filename` extension when omitted. Used to enforce the blocklist of disallowed executable / script types.

</Description>

</Field>

<Field name="content_id" type="string" prefix="attachments.">

<Description>

RFC 2392 Content-ID. When set, the attachment is rendered inline and can be referenced from the HTML body as `<img src="cid:{content_id}"/>`. When omitted, the attachment is rendered as a regular file attachment.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="scheduled_at" type="string">

<Description>

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`.

</Description>

</Field>

<Field name="contact_id" type="string">

<Description>

Preview feature — contact-targeted sends. Currently unavailable; supplying this field returns `422 UnsupportedEmailFeature`.

</Description>

</Field>

<Field name="topic_id" type="string">

<Description>

Preview feature — topic-gated sends. Currently unavailable; supplying this field returns `422 UnsupportedEmailFeature`. When generally available, a non-empty `topic_id` gates delivery on the recipient's opt-in state for that topic — if the recipient is opt_out, the send is silently suppressed and an `email.suppressed` event fires with `reason: topic_opt_out`.

</Description>

</Field>

</Payload>

<Payload kind="response">

<Field name="data" type="array of object" required>

<Description>

One entry per message in the batch, in submission order.

</Description>

<FieldChildren kind="response">

<Field name="id" type="string" prefix="data." required>

<Description>

Message ID assigned to this batch item.

</Description>

</Field>

<Field name="status" type="string" prefix="data." required>

<Description>

Initial status of this message in the batch.

</Description>

<Description>

Possible values: `accepted`

</Description>

</Field>

<Field name="category" type="string" prefix="data." required>

<Description>

Resolved category for this batch item.

</Description>

<Description>

Possible values: `marketing`, `transactional`

</Description>

</Field>

</FieldChildren>

</Field>

</Payload>