Skip to main content
import { createClient } from "@mailsai/sdk";

const client = createClient(); // reads MAILS_API_KEY

const result = await client.messages.batch([
  {
    agent: "hello",
    to: "jordan@example.com",
    subject: "Your receipt for order #10473",
    body_text: "Thanks for your purchase.",
  },
  {
    agent: "hello",
    to: "alex@example.com",
    subject: "Your receipt for order #10474",
    body_text: "Thanks for your purchase.",
  },
]);
console.log(result);
{
  "data": [
    {
      "id": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "agent_id": "agt_01JZ9F8H7KQ2M3N4P5R6S7T8U9",
      "thread_id": "thrd_01JZX8K3M9Q4P7VN2YB6RTDC11",
      "to": ["jordan@example.com"],
      "subject": "Your receipt for order #10473",
      "classifier_score": 0.01,
      "status": "sent",
      "cost_usd": 0.0004,
      "test_mode": false,
      "created_at": "2026-06-24T17:32:08.421Z"
    }
  ],
  "batch_size": 2
}

Send a batch of messages

Sends up to 100 messages in one request.
POST/v1/messages/batchscope · send
import { createClient } from "@mailsai/sdk";

const client = createClient(); // reads MAILS_API_KEY

const result = await client.messages.batch([
  {
    agent: "hello",
    to: "jordan@example.com",
    subject: "Your receipt for order #10473",
    body_text: "Thanks for your purchase.",
  },
  {
    agent: "hello",
    to: "alex@example.com",
    subject: "Your receipt for order #10474",
    body_text: "Thanks for your purchase.",
  },
]);
console.log(result);
{
  "data": [
    {
      "id": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "agent_id": "agt_01JZ9F8H7KQ2M3N4P5R6S7T8U9",
      "thread_id": "thrd_01JZX8K3M9Q4P7VN2YB6RTDC11",
      "to": ["jordan@example.com"],
      "subject": "Your receipt for order #10473",
      "classifier_score": 0.01,
      "status": "sent",
      "cost_usd": 0.0004,
      "test_mode": false,
      "created_at": "2026-06-24T17:32:08.421Z"
    }
  ],
  "batch_size": 2
}

Headers

Idempotency-Keystring

Replays the stored response for a repeated key (kept 24 hours), so a retried request never double-sends; the replay carries Idempotent-Replay: true. An error is kept on the key only once the mail has gone out, so a replayed error means it was sent. Once a scheduled send is stored, a failure after that keeps the message's answer on the key instead: the retry answers with the message (201) and raises its message.scheduled if the first request did not. After a failure before either, the same key sends again, as it does after a batch that stored no message and sent no mail.

Per-message failures are returned inline in data[] and do not fail the batch; the HTTP status is still 201. Each failure is the error a single send would answer: a message a send limit refused carries resets_at and retry_after_seconds, one the email service would not take for now (503 upstream_unavailable) retry_after_seconds, and a plan refusal upgrade_url. With an Idempotency-Key, a batch in which no message was stored and no mail went out frees the key, so sending it again with the same key sends it; any other batch's answer is kept on the key. A scheduled message is its result once stored, even if announcing it then fails: with a key the batch answers that error, and the retry with the same key gets the batch (201) and raises the message.scheduled. A batch cannot send attachments: an item carrying attachments refuses the whole batch with 422 invalid_field (param attachments), so send that message on its own with POST /v1/messages.

Response

dataobject[]

One result per submitted message, in request order: the accepted message, or an error object for one that was refused. A refused message does not stop the others.

batch_sizeinteger

How many messages the request held; data has one entry for each.

Was this page helpful?