Questions · Updated 2026-09-27
How do I send many emails in one request?
Send many emails in one request with POST /emails/batch, a JSON array of send bodies. Each item is accepted or rejected on its own, and the reply's data[i].status says which. One Idempotency-Key covers the whole request.
Send many emails in one request by calling POST /emails/batch with a JSON array, one send body per message, up to 500 items. The operation summary says "Each item succeeds or fails on its own — read data[i].status." A rejected item does not undo the others. The reply's data has one row per item with index, status (accepted or rejected), and for an accepted item its id and whether it was simulated.
Reading the reply
A rejected row carries error with code, message, fix, docs_url and details. The codes are the same ones POST /emails returns, from the error catalogue, so an item whose sender domain is not verified is rejected with domain_not_verified, an item to an address on your suppression list with suppressed_recipient, and an item with a malformed sender with invalid_from_address. The other items are accepted and go out. Match rows to what you sent by index; the order of data is the order of the request. Read fix on each rejected row and repair only those items.
curl -sS -X POST https://api.agentisend.com/emails/batch \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"201
{
"data": []
}What each item can carry
Every field of POST /emails is a field of an item: from, to, subject, html or text or template_id with template_values, cc, bcc, reply_to, headers, tags, attachments, scheduled_at and topic_id. Each to list holds up to 50 addresses. Because scheduled_at is per item, one call can place messages at different times. Because tags are per item, each message carries its own. Addresses ending in @simulator.agentisend.com are accepted in a batch like anywhere else and cost nothing, which is how to rehearse a batch before it is real.
A batch is not a mailing-list feature. There is no list or audience field here; each item names its own recipients, and each is a message to someone who asked for it.
One key for the whole call
Put an Idempotency-Key header on the request. The parameter description says "Send the same key with the same body and the original response is replayed instead of the work happening twice." That covers the array as a whole: a retry after a timeout returns the same data, accepted and rejected rows alike, and sends nothing new. Changing the array under the same key is refused with idempotency_payload_mismatch; the catalogue fix is "Reuse the exact same body for retries, or send a new Idempotency-Key for a new request." Derive the key from the job — digest-run/<week id> — not from the clock.
To take back a batch that has not gone out yet, POST /emails/bulk-cancel with the accepted ids cancels every item still scheduled or queued and lists the rest under skipped.