Send a batch

Send one letter template to up to 1,000 recipients in a single call, each letter rendered with its own data and tracked together.

Send the same letter to many recipients in one call. A batch takes a stored letter template and a recipient list, renders one letter per recipient, and groups them under a single letter_batch you can track together.

Batches use the same scopes as single letters: creating a batch needs letters:send; reading one needs letters:read.

Create a batch

POST /v1/letters/batches takes a templateId and a recipients array — one entry per letter, up to 1,000. Each recipient is a postal to address plus the optional templateData that fills the template's placeholders for that one letter. Every other option is set once and applies to every letter in the batch.

curl -X POST https://api.dairo.app/v1/letters/batches \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "ltpl_price_change",
    "recipients": [
      {
        "to": { "name": "Jane Doe", "street": "Hauptstrasse", "houseNumber": "12", "postalCode": "8001", "city": "Zürich", "country": "CH" },
        "templateData": { "firstName": "Jane", "newPrice": "CHF 49" }
      },
      {
        "to": { "name": "Hans Müller", "street": "Bahnhofstrasse", "houseNumber": "3", "postalCode": "3011", "city": "Bern", "country": "CH" },
        "templateData": { "firstName": "Hans", "newPrice": "CHF 49" }
      }
    ],
    "print": { "mode": "grayscale", "sides": "duplex" },
    "delivery": "economy",
    "notifications": false
  }'
{
  "object": "letter_batch",
  "id": "lbatch_01HZX...",
  "total": 2,
  "status": "queued"
}

Request fields

FieldRequiredDescription
templateIdyesThe stored letter template rendered once per recipient. An unknown id returns 404.
recipientsyes1–1,000 entries of { to, templateData? }. to needs a country and a street or poBox; templateData fills that letter's {{placeholders}}.
fromnoOptional sender block applied to every letter.
printnomode, sides, addressPlacement — shared by every letter. See Print & delivery options.
deliverynoDelivery class; defaults to economy. Shared by every letter.
paymentSlipnoDeclares a payment slip (qr, sepaDe, sepaAt) the template already draws, applied to every letter.
autoSendnotrue (default) queues each letter for posting; false stores them as drafts.
notificationsnoOwner milestone emails per letter; defaults to true. See Status & tracking.
metadatanoOpaque JSON object applied to every letter.

The whole request is validated before anything is created — the template id, the shared options, and every recipient's address and country. One malformed recipient fails the call with 400 or 422 and no letter is created. Past create, each letter is its own irreversible physical send: the cancellation window applies per letter, not to the batch as a whole.

Track a batch

GET /v1/letters/batches/{id} returns the batch with a live rollup: its member letters counted by status in statusCounts, and completed — the number of letters in a terminal state (delivered, undeliverable, canceled, or failed).

curl https://api.dairo.app/v1/letters/batches/lbatch_01HZX \
  -H "Authorization: Bearer $DAIRO_API_KEY"
{
  "object": "letter_batch",
  "id": "lbatch_01HZX...",
  "templateId": "ltpl_price_change",
  "status": "queued",
  "total": 2,
  "completed": 1,
  "statusCounts": { "delivered": 1, "in_transit": 1 },
  "createdAt": "2026-06-22T10:01:02Z",
  "updatedAt": "2026-06-23T08:00:11Z"
}

To work with the letters individually, list them with the batchId filter — each member is an ordinary letter you can fetch, track, and cancel one at a time on Send a letter. Each member's letter.status_changed webhook events also carry the batchId, so a webhook consumer can attribute every update to its batch.

cURL
curl "https://api.dairo.app/v1/letters?batchId=lbatch_01HZX&limit=100" \
  -H "Authorization: Bearer $DAIRO_API_KEY"

From an MCP agent

Connect an MCP-compatible agent to the hosted server and batches are available as named tools, gated by the same scopes. Sending is state-changing, so send_letters requires an explicit confirm: true.

Tool (action)OperationScope
send_letters { "action": "sendBatch" }Create a batchletters:send
list_letters { "action": "getBatch" }Get a batch's status rollupletters:read
list_letters { "action": "list", "batchId": … }List a batch's member lettersletters:read
MCP
Tool: send_letters   (scope letters:send, confirm required)
Args: {
  "action": "sendBatch",
  "templateId": "ltpl_price_change",
  "recipients": [
    { "to": { "name": "Jane Doe", "street": "Hauptstrasse", "houseNumber": "12", "postalCode": "8001", "city": "Zürich", "country": "CH" }, "templateData": { "firstName": "Jane" } }
  ],
  "delivery": "economy",
  "confirm": true
}

To project a batch's cost, price one letter with the template's page count and your delivery class. Each member is priced for its own page count, so when every recipient renders the same number of pages the batch total is the recipient count times the per-letter price.

Next steps

On this page