---
title: "Send a batch"
method: POST
path: "/v1/send/batch"
tags: ["Send"]
---

# Send a batch

`POST /v1/send/batch`

## Request

- Base URL: `https://api.volanea.com`
- URL: `https://api.volanea.com/v1/send/batch`
- Auth: HTTP bearer

Up to 1,000 personalized messages in one call. Each message succeeds or fails independently; a failed message becomes a `{ error: … }` entry in `results` rather than failing the whole batch, so a bad template id in message #7 does not stop #8.

**The batch answers 200 even when every message failed.** `results` is positional — entry *i* is the outcome of `messages[i]` — and you must inspect each entry. A caller that only checks the HTTP status reports a completely failed batch as a success. Supports `Idempotency-Key` over the whole batch.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `messages` SendMessage[], required
    - `to` union, required: Recipient address (optionally with a display name), or an array of 1–50 recipients (each gets its own message).
      - string, email
      - object
        - `email` string, email, required
        - `name` string
      - union[]
        - union
          - string, email
          - object
            - `email` string, email, required
            - `name` string
    - `subject` string: Subject line. Supports `{{variable}}` placeholders, with fallbacks: `{{firstName | "there"}}`.
    - `html` string: HTML body. Supports `{{variable}}` placeholders, with fallbacks: `{{firstName | "there"}}`.
    - `text` string: Plain-text body (wrapped in `<pre>` when no HTML is given).
    - `templateId` string: Send using a stored template instead of inline content.
    - `variables` object: Values for `{{variable}}` placeholders in the subject and body. A name you omit falls back to the same-named contact field, then to the placeholder's own `| "default"` if it has one, then to nothing.
    - `from` string, email: From address. Must belong to a verified domain (unless in test mode). Defaults to the template's from address, or hello@ your first verified domain.
    - `fromName` string
    - `replyTo` string, email: Sets a Reply-To header.
    - `headers` object: Custom email headers to include on the message.
    - `attachments` object[]: Up to 10 attachments per message. Total request size is capped at 10 MiB.
      - `filename` string, required
      - `content` string, required: Base64-encoded file content.
      - `contentType` string
    - `sendAt` string, date-time: Schedule delivery for a future time (ISO 8601 with offset). The message is stored `queued` with a `scheduledFor` — list the pending ones with `GET /v1/emails?status=scheduled`, stop one with `DELETE /v1/emails/{id}`.
    - `createContact` boolean: Set `false` to skip auto-creating a contact for unknown recipients (default `true`).
    - `type` 'transactional' | 'marketing': Marketing messages get the unsubscribe footer and skip contacts who have unsubscribed. Defaults to the template's type, or `transactional` for inline content — so promotional copy sent as inline `html` must set this explicitly or it bypasses the unsubscribe check.

## Response `200`

Per-message results, in request order.

- object
  - `results` union[], required
    - union
      - object
        - `messages` SendResult[], required
          - union: Per-recipient outcome: either an accepted message or a skip reason.
            - object
              - …
            - object
              - …
      - object: A failed message. NOT the top-level error envelope — there is no `success` here, and the human-readable text is `error.message`, not `error`. Read `error.code` to branch: `validation_error` (with `error.details` naming the fields) or `send_failed`.
        - `error` object, required
          - `message` string, required
          - `code` string, required: `validation_error` or `send_failed`.
          - `details` object[]: Failing fields, present on `validation_error` only. Paths are relative to this message, not to the batch body.
            - `path` string, required
            - `message` string, required
            - `code` string, required
  - `testMode` boolean, required
  - `replay` true: Present and `true` ONLY on an idempotent replay — absent on a first send, never `false`. Nothing was dispatched; `results` is the stored response from the original batch.

## Other responses

- `401`: Missing or invalid API key.
- `409`: `Idempotency-Key` reused with a different body (`idempotency_key_reused`) — nothing was sent.
- `422`: Request validation failed (code `validation_error`, 422).
- `429`: Per-project send rate limit (`rate_limited`). A batch consumes one unit per message, so it cannot dodge the cap.

---

[API](https://skmtc.dev/volanea/apis/volanea-api.md) · [All operations](https://skmtc.dev/volanea/apis/volanea-api/llms.txt) · [OpenAPI document](https://skmtc.dev/volanea/apis/volanea-api/revisions/9e187ae84fda?raw)
