Send a batch

POST/v1/send/batch

Secret key — sk_… or sk_test_… · Base URL https://api.volanea.com

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.

Body

Required · application/json

  • messagesobject[]required
    • toemail | object | email | object[]required

      Recipient address (optionally with a display name), or an array of 1–50 recipients (each gets its own message).

    • subjectstring

      Subject line. Supports `{{variable}}` placeholders, with fallbacks: `{{firstName | "there"}}`.

    • htmlstring

      HTML body. Supports `{{variable}}` placeholders, with fallbacks: `{{firstName | "there"}}`.

    • textstring

      Plain-text body (wrapped in `<pre>` when no HTML is given).

    • templateIdstring

      Send using a stored template instead of inline content.

    • variablesobject

      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.

    • fromemail

      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.

    • fromNamestring
    • replyToemail

      Sets a Reply-To header.

    • headersobject

      Custom email headers to include on the message.

    • attachmentsobject[]

      Up to 10 attachments per message. Total request size is capped at 10 MiB.

      • filenamestringrequired
      • contentstringrequired

        Base64-encoded file content.

      • contentTypestring
    • sendAtdate-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}`.

    • createContactboolean

      Set `false` to skip auto-creating a contact for unknown recipients (default `true`).

    • typeenum

      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.

      transactionalmarketing

Response

Per-message results, in request order.

  • resultsobject | object[]required
  • testModebooleanrequired
  • replayenum

    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.

    true

Errors

Every failure returns the standard envelope — branch on `code`, never on the wording of `error`.

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

Request

curl https://api.volanea.com/v1/send/batch \
  -X POST \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "to": "ada@example.com",
        "subject": "Your order shipped",
        "html": "<p>On its way, {{firstName}}.</p>",
        "text": "On its way, Ada.",
        "templateId": "tpl_9f2c",
        "variables": {},
        "from": "hello@yourdomain.com",
        "fromName": "Acme",
        "replyTo": "support@yourdomain.com",
        "headers": {},
        "attachments": [
          {
            "filename": null,
            "content": null,
            "contentType": null
          }
        ],
        "sendAt": "2026-01-01T09:00:00Z",
        "createContact": true,
        "type": "transactional"
      }
    ]
  }'

Response

200 · application/json

{
  "success": true,
  "data": {
    "results": [
      {
        "messages": []
      }
    ],
    "testMode": true,
    "replay": true
  }
}

92 endpoints · OpenAPI document