Public HTTPS only
Notix refuses localhost, raw IP addresses and private networks, and checks the address again before every call.
Notix posts a signed event to your server when an email is delivered or bounces, a code is checked, a text lands or a customer writes back. Your app stays in step without asking over and over.
X-Notix-Event: email.delivered X-Notix-Call: call_8k2j4h1p X-Notix-Timestamp: 1791359201000 X-Notix-Signature: v1=5f2c9a...e41b X-Notix-Retry: true
Every call has the same outer shape: an id, the type, the time and the event's own data. Pick a group to see its events and a sample payload.
{
"id": "call_8k2j4h1p",
"type": "email.delivered",
"version": "2026-09-08",
"createdAt": "2026-10-07T09:41:04.000Z",
"teamId": 123,
"data": {
"id": "cm9x2k7f0001",
"from": "Acme <orders@acme.com>",
"to": [
"amara@example.com"
],
"subject": "Your order is on its way",
"occurredAt": "2026-10-07T09:41:03.951Z",
"status": "DELIVERED"
},
"attempt": 1
}
Each webhook has its own secret. The SDKs check the signature and the timestamp, then hand you a typed event.
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY);
const hooks = notix.webhooks(process.env.NOTIX_WEBHOOK_SECRET);
export async function POST(request: Request) {
// Throws if the signature or the timestamp is wrong.
const event = hooks.constructEvent(await request.text(), {
headers: request.headers,
});
if (event.type === "email.bounced") {
await markAddressBad(event.data.to);
}
return new Response(null, { status: 200 });
}
import os
from flask import Flask, request
from notix import Notix
app = Flask(__name__)
notix = Notix(os.environ["NOTIX_API_KEY"])
hooks = notix.webhooks(os.environ["NOTIX_WEBHOOK_SECRET"])
@app.post("/hooks/notix")
def notix_hook():
event = hooks.construct_event(request.data, headers=request.headers)
if event["type"] == "email.bounced":
mark_address_bad(event["data"]["to"])
return "", 200
<?php
use Notix\Notix;
$notix = new Notix(getenv('NOTIX_API_KEY'));
$hooks = $notix->webhooks(getenv('NOTIX_WEBHOOK_SECRET'));
$event = $hooks->constructEvent(file_get_contents('php://input'), getallheaders());
if ($event['type'] === 'email.bounced') {
markAddressBad($event['data']['to']);
}
http_response_code(200);
No SDK in your language? The signature is an HMAC-SHA256 of the timestamp and the raw body. The verify guide shows it by hand.
A short outage on your side does not lose events.
If your endpoint answers with anything but a 2xx, takes longer than 10 seconds or cannot be reached, Notix tries again after about 5, 10, 20, 40 and 80 seconds. A retry carriesX-Notix-Retry: true and the same call id.
Everything on the Webhooks page is in the API too: create, change, pause, test, delete, read the call log and retry a call. An empty eventTypes list sends every event.
const { data, error } = await notix.webhookEndpoints.create({
url: "https://acme.com/hooks/notix",
eventTypes: ["email.delivered", "email.bounced", "contact.unsubscribed"],
});
// Shown once. Keep it as NOTIX_WEBHOOK_SECRET.
console.log(data?.secret);
// Send a signed webhook.test event to the saved URL.
await notix.webhookEndpoints.test(webhookId);
// Read the delivery log and send a failed call again.
const { data: calls } = await notix.webhookEndpoints.listCalls({ webhookId, status: "FAILED" });
await notix.webhookEndpoints.retryCall(callId);
// Leaked a secret? Rotate it. The old one stops at once.
await notix.webhookEndpoints.rotateSecret(webhookId);
Notix refuses localhost, raw IP addresses and private networks, and checks the address again before every call.
Shown once when you create it. Rotate it whenever you like; the old one stops at once.
Press Send a test event and a signed webhook.test call lands on your URL and in the log.
Send, batch, schedule and retry safely with idempotency keys, then follow every message through logs, events and signed webhooks.
Explore Email APIDelivery, open, click and bounce numbers over time, every email's event history, and 30 days of API request logs.
Explore Metrics and logsReceive 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.