Send a batch of emails
Send up to 100 emails in one request.
https://app.usenotix.dev/api/v1/emails/batchFull access and sending access keys can call this. Key permissions lists the extra rules some calls have.
Send up to 100 emails in one request. Every email in the batch carries the same limits as a single send.
| Scope | Limit |
|---|---|
| Request body | 20 MB, applied at the edge |
| Emails per request | 100 |
| Recipients per message | 50 across to, cc and bcc |
attachments total across the batch | 40 MB, once decoded from base64 |
The recipient cap is per message, not per request: a batch of 100 messages naming three people each is fine, and one message in it naming 51 refuses the whole batch with 422. Every recipient counts as one email against the team's quota, so that batch reserves and is billed 300.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | The request is invalid, including any size limit above. |
| 401 | UNAUTHORIZED | The API key is missing or not valid. |
| 403 | FORBIDDEN | The API key is restricted to a domain other than a from address. |
| 413 | The request body is over 20 MB. | |
| 422 | UNPROCESSABLE_ENTITY | A message in the batch names more than 50 recipients. |
| 429 | RATE_LIMITED | The per second rate limit or the team's send limit was reached. |
Idempotency
Pass an Idempotency-Key header to make a retry safe for 24 hours. The same key and body answers the original result without sending again, with the response header Idempotent-Replayed: true. The same key with a different body answers 409 NOT_UNIQUE; the same key while the first request is still running answers 409 too, so retry shortly. Keys are scoped to the API key.
Dry run
Send the header Notix-Dry-Run: true to see what the request would do without doing it. The answer is 200 with dryRun: true: who it would reach, what they would see, what it would cost, a verdict (allowed and every reasons it would be refused for) and the limits it would count against. Nothing is sent, charged, reserved or written, and the call is recorded in the audit log as a dry run. A write route that cannot preview answers 400.
Body parameters
The visible recipients. Optional when cc or bcc names somebody, which is how a Bcc-only message is sent. At most 50 distinct addresses across to, cc and bcc, and every one of them counts as an email against the team's quota.
Optional when templateId is provided. At most 998 characters.
At most 998 characters.
ID of a template from the dashboard. A template carries its own subject, preview text and type (transactional or marketing); pass variables to fill it. A link that is exactly one variable, such as {{actionUrl}}, is only filled with an http(s), mailto or tel address.
Values for the template's variables: at most 200, each at most 10000 characters.
Copied recipients. Counted with to and bcc against the 50 recipient cap and against the team's quota.
Blind copied recipients. Counted with to and cc against the 50 recipient cap and against the team's quota.
Plain text body. At most 2,000,000 characters.
At most 2,000,000 characters. Can be null.
HTML body. At most 2,000,000 characters.
At most 2,000,000 characters. Can be null.
Custom headers to include with the email: at most 50, each name at most 100 characters and each value at most 2,000. A name must be a plain header name (letters, digits and - ; no spaces, colons or control characters) and a value may not contain control characters such as line breaks; either answers 400. Identity and trace headers (From, Sender, Reply-To, Return-Path, Message-ID, DKIM-Signature, ARC-*, Authentication-Results, Received and similar) are set by Notix and are dropped if sent here.
Up to 10 files, each at most 7 MB decoded, and at most 10 MB decoded in total.
At most 255 characters.
Can be null.
Headers
Bearer followed by your API key.
Pass the optional Idempotency-Key header to make the request safe to retry. The key can be up to 256 characters. The server stores the canonical request body and behaves as follows: - Same key + same request body → returns the original emailId with 200 OK without re-sending, and the response header Idempotent-Replayed: true. - Same key + different request body → returns 409 Conflict with code: NOT_UNIQUE so you can detect the mismatch. - Same key while another request is still being processed → returns 409 Conflict; retry after a short delay or once the first request completes. Entries expire after 24 hours. Use a unique key per logical send (for example, an order or signup ID).
Set to true to preview the request instead of running it. The answer is 200 with dryRun: true: who it would reach, what they would see, what it would cost, whether policy allows it and the limits it counts against. Nothing is sent, charged, reserved or written. A write route that cannot preview answers 400.
Errors
The message names more than 50 distinct recipients across to, cc and bcc.
Every error has the same shape. See all error codes.
curl -X POST "https://app.usenotix.dev/api/v1/emails/batch" \
-H "Authorization: Bearer $NOTIX_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{
"from": "Acme <receipts@acme.com>"
}
]'
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY);
const { data, error } = await notix.emails.batch([
{ from: "Acme <hello@acme.com>", to: "ada@example.com", subject: "Welcome", html: "<p>Hi Ada</p>" },
{ from: "Acme <hello@acme.com>", to: "tunde@example.com", subject: "Welcome", html: "<p>Hi Tunde</p>" },
]);
from notix import Notix
notix = Notix() # reads NOTIX_API_KEY
data, _ = notix.emails.batch([
{"from": "Acme <hello@acme.com>", "to": "ada@example.com", "subject": "Welcome", "html": "<p>Hi Ada</p>"},
{"from": "Acme <hello@acme.com>", "to": "tunde@example.com", "subject": "Welcome", "html": "<p>Hi Tunde</p>"},
])
use Notix\Notix;
$notix = new Notix(getenv('NOTIX_API_KEY'));
$result = $notix->emails->batch([
['from' => 'Acme <hello@acme.com>', 'to' => 'ada@example.com', 'subject' => 'Welcome', 'html' => '<p>Hi Ada</p>'],
['from' => 'Acme <hello@acme.com>', 'to' => 'tunde@example.com', 'subject' => 'Welcome', 'html' => '<p>Hi Tunde</p>'],
]);
{
"data": [
{
"emailId": "cm1x8f3k20001qz7v5b9d2e4a"
}
],
"results": [
{
"index": 1,
"emailId": "cm1x8f3k20001qz7v5b9d2e4a",
"status": "QUEUED",
"error": "string"
}
],
"summary": {
"total": 1,
"queued": 1,
"scheduled": 1,
"suppressed": 1,
"failed": 1
}
}