Documentation
Sign inGet started

Send an email message

POST
/v1/email/messages
const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  subject: "Hello from Bird",
  html: "<p>My first Bird email.</p>",
});
console.log(msg.id, msg.status); // "em_…", "accepted"
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 Send a batch of messages instead. The category field controls suppression policy independently of content: set it to marketing when sending marketing content from this endpoint.
The 202 response means the message is safely accepted for delivery, not yet delivered. 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 covers its recipient and volume limits.
Corps de la requête
from
string or object
obligatoire
Sender address, as a plain email string, an RFC 5322 mailbox string (Jane <jane@acme.com>), or an object with an optional display name. Must be from a verified domain in this workspace.
Afficher les paramètres enfants
from.email
string
obligatoire
Email address.
from.name
string
Display name shown alongside the address in mail clients.
to
array of string or object
obligatoire
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.
Afficher les paramètres enfants
to.email
string
obligatoire
Email address.
to.name
string
Display name shown alongside the address in mail clients.
cc
array of string or object
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.
Afficher les paramètres enfants
cc.email
string
obligatoire
Email address.
cc.name
string
Display name shown alongside the address in mail clients.
bcc
array of string or object
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.
Afficher les paramètres enfants
bcc.email
string
obligatoire
Email address.
bcc.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
array of string or object
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.
Afficher les paramètres enfants
reply_to.email
string
obligatoire
Email address.
reply_to.name
string
Display name shown alongside the address in mail clients.
headers
object
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 the headers generated for you automatically (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 a marketing send a compliant unsubscribe header is set for you, so supplying either one there is rejected with a 422. Header values may not contain carriage-return or line-feed characters. Up to 25 headers per send, each value up to 998 characters.
tags
array of object
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.
Afficher les paramètres enfants
tags.name
string
obligatoire
Tag name. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 32 characters.
tags.value
string
obligatoire
Tag value. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 64 characters.
metadata
object
Arbitrary JSON object stored, returned on API reads, and echoed in webhook payloads. Path-queryable in analytics (for example, 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.
parameters
object
Parameter values used to personalize inline content. A parameter is a single word, and a token in the subject or body (for example {{ animal }}) is replaced with the value of that name 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.
template
object
Send a stored template instead of inline content. When set, omit subject, html and text, because the template supplies them. Personalize with template.parameters. A template send goes out immediately: template and scheduled_at are mutually exclusive, and combining them is rejected with a 422.
Afficher les paramètres enfants
template.id
string
obligatoire
The template to send, by its id.
template.slug
string
obligatoire
The template to send, by its slug handle. A workspace template (for example welcome-email) or a built-in system template (for example bird_welcome).
template.language
string
Which of the template's languages to send. Omit it to send the template's default language, unless the template sets language_source_required, in which case a send naming no language is rejected. When the template does not have the language you ask for, its own on_missing_language setting decides whether the closest available language is sent instead or the send is rejected.
template.parameters
object
Values for the template's variables, keyed by the variable name. A variable name is a single word.
Every variable the template's variables lists needs a value here. A send that leaves one out is rejected rather than delivered with a blank in it. Send values for everything in that list rather than only what you expect the language you are sending to use, because languages do not have to use the same variables and a value no language uses is simply ignored.
bird is reserved for the values we fill in ourselves, 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
string
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.
When you send with template and omit this field, the message takes the template's own classification, so a template created as transactional sends as transactional. Set this field to classify a single send differently from its template. It always takes precedence. A send with no template and no category defaults to marketing.
Possible values: marketing, transactional
attachments
array of object
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.
Afficher les paramètres enfants
attachments.filename
string
obligatoire
The name the recipient sees on the attachment.
attachments.content
string
obligatoire
The file's bytes, base64-encoded. What you send here counts toward the message's 20 MB limit after encoding and MIME wrapping, not at its raw size.
attachments.content_type
string
The file's MIME type. Leave it out and we work it out from the extension on filename. This is what we check against the list of executable and script types we refuse.
attachments.content_id
string
An RFC 2392 Content-ID for the file. Set it and the attachment is shown inline, so your HTML body can point at it with <img src="cid:{content_id}"/>. Leave it out and the file arrives as an ordinary attachment the recipient downloads.
scheduled_at
string
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. A scheduled message has inline content: scheduled_at and template are mutually exclusive, and combining them is rejected with a 422. This field is only accepted on a single send, not on a batch item.
Contenu de la réponse
id
string
obligatoire
Message ID.
from
object
obligatoire
Sender address. name is present when a display name was provided on the send.
Afficher les attributs enfants
from.email
string
obligatoire
Email address.
from.name
string
Display name shown alongside the address in mail clients.
to
array of object
obligatoire
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.
Afficher les attributs enfants
to.email
string
obligatoire
Email address.
to.name
string
Display name shown alongside the address in mail clients.
cc
array of object
CC recipients.
Afficher les attributs enfants
cc.email
string
obligatoire
Email address.
cc.name
string
Display name shown alongside the address in mail clients.
bcc
array of object
BCC recipients.
Afficher les attributs enfants
bcc.email
string
obligatoire
Email address.
bcc.name
string
Display name shown alongside the address in mail clients.
subject
string
obligatoire
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
string
obligatoire
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.
Possible values: marketing, transactional
reply_to
nullable array
Reply-To addresses, if set on the send. Empty/null when no Reply-To was provided.
status
string
obligatoire
accepted_count
integer
obligatoire
How many recipients are in the accepted state, meaning we have the message and are getting ready to deliver it.
processed_count
integer
obligatoire
How many recipients the message has been prepared for and queued for delivery.
delivered_count
integer
obligatoire
How many recipients' messages were accepted by their mail server.
bounced_count
integer
obligatoire
Number of recipients that resulted in a permanent delivery failure.
complained_count
integer
obligatoire
Number of recipients that reported spam.
deferred_count
integer
obligatoire
Number of recipients in transient delivery deferral. Their mail server asked for a retry, and delivery attempts continue.
rejected_count
integer
obligatoire
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
nullable integer
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
nullable integer
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
nullable integer
End-to-end accept → delivered time for the fastest delivered recipient, in milliseconds. Null until the first recipient is delivered.
open_count
integer
obligatoire
Total open events across all recipients.
click_count
integer
obligatoire
Total click events across all recipients.
requested_language
nullable string
The template language this send asked for, in canonical form (pt-BR for a request of pt-br). Null when the send named no language (it took the template's default) or used no template at all. Compare it with resolved_language: when they differ, the language you asked for was not available and the template's on_missing_language policy chose the one shown there instead.
resolved_language
nullable string
The template language this send was actually delivered in, in canonical form. Null when the send used no template. A non-null value with a null requested_language means the send named no language and took the template's default.
template_id
nullable string
The template this send rendered from, or null for a send that supplied its content inline.
template_version_id
nullable string
The exact template version this send rendered from, or null for an inline send. A template's live version changes every time you submit it, so this is what identifies the wording that was actually delivered, together with resolved_language.
tags
array of object
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.
Afficher les attributs enfants
tags.name
string
obligatoire
Tag name. ASCII letters, digits, underscore, and hyphen only. Case-sensitive. Maximum 32 characters.
tags.value
string
obligatoire
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
nullable object
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
array of object
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.
Afficher les attributs enfants
attachments.id
string
Attachment ID, stable per email send.
attachments.filename
string
obligatoire
Filename as shown to the recipient.
attachments.content_type
string
Resolved MIME type at send time.
attachments.size
integer
obligatoire
Decoded size in bytes.
attachments.inline
boolean
True when the attachment was sent inline via a content_id reference in the HTML body, false for regular file attachments.
attachments.content_id
nullable string
The Content-ID set at send time, when the attachment was inline.
track_opens
boolean
obligatoire
Whether open tracking is enabled for this send.
track_clicks
boolean
obligatoire
Whether click tracking is enabled for this send.
created_at
string
obligatoire
When the send request was accepted.
thread_id
nullable string
Thread this message belongs to, or null when the message is not part of one.
in_reply_to_message_id
nullable string
The message this one is a reply to, if any.
delivered_at
nullable string
When all recipients reached a terminal delivered state, or null if not yet fully delivered.
scheduled_at
nullable string
When this message is scheduled to send, for a send created with a future send time. Null for an immediate send. Stays set after the scheduled send fires.