Send multiple messages with POST /v2/email_messages/batch. Each item in the messages array is validated independently. Read the result of every item before deciding whether to retry.
Limits and request shape
A batch contains 1 to 1,000 messages. A request with 1,001 messages, an empty array, or a malformed batch envelope returns 400. The item cap counts messages, while your daily sending quota counts recipients; these are separate limits.
Each message still has the decoded size limits described in Understanding Telnyx Email errors and message size limits. Keep attachment-heavy batches small enough for the applicable request-size limits as well.
curl -X POST "https://api.telnyx.com/v2/email_messages/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: YOUR_UNIQUE_BATCH_KEY" \
-d '{
"messages": [
{
"from": "sender@mail.yourcompany.com",
"to": ["first@example.com"],
"subject": "Order update",
"text_body": "Your first order has shipped."
},
{
"from": "sender@mail.yourcompany.com",
"to": ["second@example.com"],
"subject": "Order update",
"text_body": "Your second order has shipped."
}
]
}'
Use your verified sender and permitted recipients. Trial-account recipient restrictions still apply to production sends.
Always inspect the 207 results
Once the batch is processed, the response is always 207 Multi-Status, whether every item succeeds, some fail, or every item fails. Request-level rejections, such as the 1,001-item validation failure, are separate from this per-item result contract.
datacontains the created messages and their IDs/statuses. It may be empty.errorscontains failed items with a zero-basedindex,code, andmessage. Map each index back to the originalmessagesarray, not to the shorterdataarray.meta.total,meta.succeeded, andmeta.failedsummarize the result.
For example, if the first item was accepted and the second had missing required fields:
{
"data": [
{
"record_type": "email_message",
"id": "11111111-1111-4111-8111-111111111111",
"status": "queued"
}
],
"errors": [
{
"index": 1,
"code": "bad_request",
"message": "from, to, and subject are required"
}
],
"meta": {"total": 2, "succeeded": 1, "failed": 1}
}
Acceptance is not delivery. Save the IDs from data and check recipient events or webhooks. A scheduled item is accepted as scheduled for a future send, an immediate item as queued, and a sandbox item as sandbox without real delivery. These variants keep their own status and lifecycle semantics inside the batch's 207 response.
Retry without duplicating accepted messages
- Generate one
Idempotency-KeyHTTP header for each logical batch request. The key covers the whole request, not individual items; there are no per-message keys insidemessages. - If the response is lost, retry the identical body with the same key. A successful replay preserves the original HTTP status and body. Replaying a recorded
207does not rerun its failed items. - After receiving results, keep all accepted message IDs. Correct the failed items and submit only those items in a new batch with a new key. The new array has its own zero-based indexes; retain your original-to-retry mapping.
- Inspect each failure before retrying. Invalid fields need correction; suppressed recipients and policy failures need remediation. Do not resubmit accepted messages merely because another item failed.
See Preventing duplicate emails with idempotency for replay behavior and key errors.