Emails.
Send one email or a batch, read them back, and change or cancel a scheduled send. Every request needs an API key in the Authorization header; the reference overview covers keys, errors and rate limits.
Send an email
POST/v1/emailsSending or full access key
Send one transactional email. The response carries the emailId every webhook event for the message refers to.
Limits
Every send request is bounded. The checks run before the email is stored, so a request that breaks one of them costs nothing and creates nothing.
| Field | Limit |
|---|---|
| Request body | 20 MB, applied at the edge |
to, cc, bcc | 50 recipients across the three fields, counted on distinct addresses |
subject | 998 characters |
html | 2,000,000 characters |
text | 2,000,000 characters |
attachments | 10 files |
attachments[].filename | 255 characters |
attachments[].content | 7 MB per file, once decoded from base64 |
attachments total | 10 MB per email, once decoded from base64 |
Attachment sizes are measured after base64 decoding, so a 7 MB file is about 9.4 MB of base64 in the request body.
Recipients
to, cc and bcc each take a single address or an array of them, and the three together may name at most 50 distinct recipients. The same address in two fields is one recipient: addresses are compared case insensitively, and a display-name form (Ada Lovelace <ada@example.com>) is compared on the address inside it.
Every recipient counts as one email against the team's daily and monthly send limits and on the bill. A recipient on the team's suppression list is not sent to and is not counted. A message naming more than 50 is refused with 422 before anything is sent:
{
"error": {
"code": "UNPROCESSABLE_ENTITY",
"message": "A message can name at most 50 recipients across to, cc and bcc; this one names 63."
}
}
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | The request is invalid, including any size limit above. The body names the field and the limit. |
| 401 | UNAUTHORIZED | The API key is missing or not valid. |
| 403 | FORBIDDEN | The API key is restricted to a domain other than the from address. |
| 413 | The request body is over 20 MB. It is refused at the edge, with no JSON body. | |
| 422 | UNPROCESSABLE_ENTITY | The message names more than 50 recipients across to, cc and bcc. |
| 429 | RATE_LIMITED | The per second rate limit or the team's send limit was reached. |
| Field | Type | About |
|---|---|---|
Idempotency-Key | string | 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. - 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). |
| Field | Type | About |
|---|---|---|
to | string or string[] | 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. |
fromrequired | string | |
subject | string | Optional when templateId is provided. At most 998 characters. At most 998 characters. |
templateId | string | 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. |
variables | map of string | Values for the template's variables: at most 200, each at most 10000 characters. |
replyTo | string or string[] | |
cc | string or string[] | Copied recipients. Counted with to and bcc against the 50 recipient cap and against the team's quota. |
bcc | string or string[] | Blind copied recipients. Counted with to and cc against the 50 recipient cap and against the team's quota. |
text | string | Plain text body. At most 2,000,000 characters. At most 2,000,000 characters. Can be null. |
html | string | HTML body. At most 2,000,000 characters. At most 2,000,000 characters. Can be null. |
headers | map of string | Custom headers to included with the emails |
attachments | object[] | Up to 10 files, each at most 7 MB decoded, and at most 10 MB decoded in total. |
attachments[].filenamerequired | string | At most 255 characters. |
attachments[].contentrequired | string | |
scheduledAt | string (date-time) | |
inReplyToId | string | Can be null. |
curl -X POST "https://app.usenotix.dev/api/v1/emails" \
-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.send(
{
from: "Acme <receipts@acme.com>",
to: "customer@example.com",
subject: "Your receipt for order 4471",
html: "<p>Thanks for your order.</p>",
text: "Thanks for your order.",
},
{ idempotencyKey: "order-4471-receipt" },
);
from notix import Notix
notix = Notix() # reads NOTIX_API_KEY
data, _ = notix.emails.send(
{
"from": "Acme <receipts@acme.com>",
"to": "customer@example.com",
"subject": "Your receipt for order 4471",
"html": "<p>Thanks for your order.</p>",
"text": "Thanks for your order.",
},
{"idempotency_key": "order-4471-receipt"},
)
use Notix\Notix;
$notix = new Notix(getenv('NOTIX_API_KEY'));
$email = $notix->emails->send([
'from' => 'Acme <receipts@acme.com>',
'to' => 'customer@example.com',
'subject' => 'Your receipt for order 4471',
'html' => '<p>Thanks for your order.</p>',
'text' => 'Thanks for your order.',
], 'order-4471-receipt');
Responses
200The email was accepted and queued.
| Field | Type | About |
|---|---|---|
emailId | string |
422The message names more than 50 distinct recipients across to, cc and bcc.
| Field | Type | About |
|---|---|---|
errorrequired | object | |
error.coderequired | string | One of |
error.messagerequired | string |
Send a batch of emails
POST/v1/emails/batchSending or full access key
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. |
| Field | Type | About |
|---|---|---|
Idempotency-Key | string | 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. - 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). |
| Field | Type | About |
|---|---|---|
to | string or string[] | 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. |
fromrequired | string | |
subject | string | Optional when templateId is provided. At most 998 characters. At most 998 characters. |
templateId | string | 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. |
variables | map of string | Values for the template's variables: at most 200, each at most 10000 characters. |
replyTo | string or string[] | |
cc | string or string[] | Copied recipients. Counted with to and bcc against the 50 recipient cap and against the team's quota. |
bcc | string or string[] | Blind copied recipients. Counted with to and cc against the 50 recipient cap and against the team's quota. |
text | string | Plain text body. At most 2,000,000 characters. At most 2,000,000 characters. Can be null. |
html | string | HTML body. At most 2,000,000 characters. At most 2,000,000 characters. Can be null. |
headers | map of string | Custom headers to included with the emails |
attachments | object[] | Up to 10 files, each at most 7 MB decoded, and at most 10 MB decoded in total. |
attachments[].filenamerequired | string | At most 255 characters. |
attachments[].contentrequired | string | |
scheduledAt | string (date-time) | |
inReplyToId | string | Can be null. |
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>'],
]);
Responses
200List of successfully created email IDs
| Field | Type | About |
|---|---|---|
datarequired | object[] | |
data[].emailIdrequired | string |
422The message names more than 50 distinct recipients across to, cc and bcc.
| Field | Type | About |
|---|---|---|
errorrequired | object | |
error.coderequired | string | One of |
error.messagerequired | string |
Get an email
GET/v1/emails/{emailId}Sending or full access key
Retrieve one email with its latest status. A sending access key can read only the emails it sent.
| Field | Type | About |
|---|---|---|
emailIdrequired | string |
curl -X GET "https://app.usenotix.dev/api/v1/emails/<emailId>" \
-H "Authorization: Bearer $NOTIX_API_KEY"
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY);
const { data, error } = await notix.emails.get("email_123");
from notix import Notix
notix = Notix() # reads NOTIX_API_KEY
data, _ = notix.emails.get("email_123")
use Notix\Notix;
$notix = new Notix(getenv('NOTIX_API_KEY'));
$email = $notix->emails->get('email_123');
Responses
200Retrieve the email
| Field | Type | About |
|---|---|---|
idrequired | string | |
teamIdrequired | number | |
torequired | string or string[] | |
replyTo | string or string[] | |
cc | string or string[] | |
bcc | string or string[] | |
fromrequired | string | |
subjectrequired | string | |
htmlrequired | string | Can be null. |
textrequired | string | Can be null. |
createdAtrequired | string | |
updatedAtrequired | string | |
emailEventsrequired | object[] | |
emailEvents[].emailIdrequired | string | |
emailEvents[].statusrequired | string | One of |
emailEvents[].createdAtrequired | string | |
emailEvents[].data | any | Can be null. |
List emails
GET/v1/emailsFull access key
List the team's emails, newest first, filtered and paged by the query parameters.
| Field | Type | About |
|---|---|---|
page | integer | Default |
limit | integer | Default |
startDate | string | |
endDate | string | |
domainId | string |
curl -X GET "https://app.usenotix.dev/api/v1/emails" \
-H "Authorization: Bearer $NOTIX_API_KEY"
Responses
200Retrieve a list of emails
| Field | Type | About |
|---|---|---|
datarequired | object[] | |
data[].idrequired | string | |
data[].torequired | string or string[] | |
data[].replyTo | string or string[] or any | |
data[].cc | string or string[] or any | |
data[].bcc | string or string[] or any | |
data[].fromrequired | string | |
data[].subjectrequired | string | |
data[].htmlrequired | string | Can be null. |
data[].textrequired | string | Can be null. |
data[].createdAtrequired | string | |
data[].updatedAtrequired | string | |
data[].latestStatusrequired | string | One of |
data[].scheduledAtrequired | string (date-time) | Can be null. |
data[].domainIdrequired | number | Can be null. |
countrequired | number |
Reschedule an email
PATCH/v1/emails/{emailId}Full access key
Change scheduledAt on an email that has not been sent yet.
| Field | Type | About |
|---|---|---|
emailIdrequired | string |
| Field | Type | About |
|---|---|---|
scheduledAtrequired | string (date-time) |
curl -X PATCH "https://app.usenotix.dev/api/v1/emails/<emailId>" \
-H "Authorization: Bearer $NOTIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scheduledAt": "2026-10-01T09:00:00Z"
}'
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY);
const { data, error } = await notix.emails.update("email_123", {
scheduledAt: "2026-10-01T09:00:00Z",
});
from notix import Notix
notix = Notix() # reads NOTIX_API_KEY
data, _ = notix.emails.update("email_123", {"scheduledAt": "2026-10-01T09:00:00Z"})
use Notix\Notix;
$notix = new Notix(getenv('NOTIX_API_KEY'));
$email = $notix->emails->update('email_123', ['scheduledAt' => '2026-10-01T09:00:00Z']);
Responses
200The email was rescheduled.
| Field | Type | About |
|---|---|---|
emailId | string |
Cancel a scheduled email
POST/v1/emails/{emailId}/cancelFull access key
Cancel an email that is scheduled and has not been sent yet.
| Field | Type | About |
|---|---|---|
emailIdrequired | string |
curl -X POST "https://app.usenotix.dev/api/v1/emails/<emailId>/cancel" \
-H "Authorization: Bearer $NOTIX_API_KEY"
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY);
const { data, error } = await notix.emails.cancel("email_123");
from notix import Notix
notix = Notix() # reads NOTIX_API_KEY
data, _ = notix.emails.cancel("email_123")
use Notix\Notix;
$notix = new Notix(getenv('NOTIX_API_KEY'));
$email = $notix->emails->cancel('email_123');
Responses
200The scheduled email was cancelled.
| Field | Type | About |
|---|---|---|
emailId | string |