Schedule
Hold a message until a time you choose, then move it or cancel it before it goes.
scheduledAtOne call sends a receipt, a reset link or a newsletter. Then every step it takes is in your logs, on the Emails page and at your webhook, so you never have to guess what happened to a message.
const { data } = await notix.emails.send({ from: "orders@acme.com", to: "amara@example.com", subject: "Your order is on its way", html: "<p>Tracking: KD-4471</p>", }, { idempotencyKey: "order-4471" }); // data.emailId: "cm9x2k..."
Three SDKs that set the key and the headers for you, and a plain JSON API for everything else. Each call returns an emailId you can look up later.
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY);
const { data, error } = await notix.emails.send({
from: "Acme <orders@acme.com>",
to: "amara@example.com",
subject: "Your order is on its way",
html: "<p>Your parcel left our warehouse today.</p>",
});
console.log(data?.emailId);
import os
from notix import Notix
notix = Notix(os.environ["NOTIX_API_KEY"])
data, error = notix.emails.send({
"from": "Acme <orders@acme.com>",
"to": "amara@example.com",
"subject": "Your order is on its way",
"html": "<p>Your parcel left our warehouse today.</p>",
})
<?php
use Notix\Notix;
$notix = new Notix(getenv('NOTIX_API_KEY'));
$email = $notix->emails->send([
'from' => 'Acme <orders@acme.com>',
'to' => 'amara@example.com',
'subject' => 'Your order is on its way',
'html' => '<p>Your parcel left our warehouse today.</p>',
]);
echo $email['emailId'];
curl -X POST https://app.usenotix.dev/api/v1/emails \
-H "Authorization: Bearer $NOTIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <orders@acme.com>",
"to": "amara@example.com",
"subject": "Your order is on its way",
"html": "<p>Your parcel left our warehouse today.</p>"
}'
The same send also works over SMTP, with no code change at all. The full request and response are in the API reference.
Logs for the requests you made, events for what happened after, and webhooks that tell your own system.
Every call to the API is written to the Logs page with its status, the key that made it and the time, and kept for 30 days. So a failing call in staging is found in seconds, not in a support thread.
notix.logs.list()POST /v1/emails200POST /v1/emails/batch200POST /v1/emails422PATCH /v1/emails/cm9x...200GET /v1/emails/cm9x...200Open any email on the Emails page and you see each step it took: queued, sent, delivered, opened, clicked, or the bounce and the reason for it. The same history is one notix.emails.get(id) away.
Register a URL and Notix posts each event to it as it happens. Every delivery carries anX-Notix-Signature you check with your secret, and a failed delivery is tried again up to six times. Calls you missed can be sent again by hand from the console.
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY);
const hooks = notix.webhooks(process.env.NOTIX_WEBHOOK_SECRET);
// Throws if the signature or timestamp is wrong.
const event = hooks.constructEvent(await request.text(), { headers: request.headers });
All event types are on the webhooks page and in the webhooks guide.
POST /hooks/notix HTTP/1.1
X-Notix-Event: email.delivered
X-Notix-Timestamp: 1791359263951
X-Notix-Signature: v1=5f2c9a...e41b
{
"id": "call_8sd02k",
"type": "email.delivered",
"version": "2026-09-08",
"createdAt": "2026-10-07T09:41:03.951Z",
"teamId": 123,
"attempt": 1,
"data": {
"id": "cm9x2k7f0001",
"status": "DELIVERED",
"from": "orders@acme.com",
"to": ["amara@example.com"],
"subject": "Your order is on its way",
"occurredAt": "2026-10-07T09:41:03.951Z"
}
}
The parts of a sending system you would otherwise build yourself.
Hold a message until a time you choose, then move it or cancel it before it goes.
scheduledAtSend up to 100 different messages in one request, each with its own recipient and content.
100 per callInvoices and tickets as base64 files: up to 10 per email, 7 MB each.
10 files, 10 MBRetry without fear. The same Idempotency-Key never sends the same email twice.
24 hoursSend a saved template by its id and fill in {{firstName}} and friends from variables.
Up to 50 addresses across to, cc and bcc, a reply-to, and your own headers.
50 recipientsA scheduled email waits in Notix, not in a cron job of yours. It shows on the Emails page as scheduled until it sends.
const { data } = await notix.emails.send({
from: "Acme <hello@acme.com>",
to: "wanjiru@example.com",
subject: "Your trial ends tomorrow",
html: "<p>Here is what happens next.</p>",
scheduledAt: "2026-10-08T09:00:00+03:00",
});
// Changed your mind? Move it or call it off before it goes.
await notix.emails.update(data.emailId, { scheduledAt: "2026-10-08T12:00:00+03:00" });
await notix.emails.cancel(data.emailId);
One request, a hundred shipping notices, each filled in with its own name and tracking code. The template is the one your team edits in the console.
await notix.emails.batch(
orders.map((order) => ({
from: "Acme <orders@acme.com>",
to: order.email,
templateId: "order-shipped",
variables: { firstName: order.firstName, trackingCode: order.tracking },
})),
);
Networks drop answers. With an idempotency key, the retry gets the first result back instead of a second email.
The first request sends. Every retry with the same key and the same body gets that answer back, marked as replayed. Change the body and keep the key, and the API refuses it, so a bug cannot reuse a key by accident.
emails.send(payload, { idempotencyKey }){
"to": "amara@example.com",
"subject": "Your receipt",
...
}An address that bounced hard, complained or unsubscribed is not sent to again, from the API, SMTP or a broadcast.
Mail goes out from your verified domain with SPF, DKIM and DMARC in place. How deliverability works.
A dry run checks a send and shows who it would reach, without sending anything. Test emails.
Point any framework or tool at one SMTP host with your API key as the password, and get the same tracked, suppressed send as the API.
Explore SMTP relaySend a sign-in or verification code by SMS or email, score the request for risk first, and check the code in one call.
Explore One-time codesReceive mail on your own domain, read it in the console or by API and webhook, and forward it on with an optional forwarded-message header.
Explore Inbound emailVerify a domain, copy an API key, make one call. The free plan does not ask for a card.