Notix in a Next.js app, from server action to webhook.
Next.js runs your code in two places, and only one of them may hold an API key. This page puts the send in a server action and a route handler, keeps the key in a server-only variable, and adds the webhook route that tells you what happened to each message after it left.
Send an email from Next.js.
"use server";
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY!);
export async function sendReceipt(to: string, orderId: string) {
const { data, error } = await notix.emails.send(
{
from: "Acme <receipts@acme.com>",
to,
subject: `Your receipt for order ${orderId}`,
html: "<p>Thanks for your order.</p>",
},
{ idempotencyKey: `order-${orderId}-receipt` },
);
if (error) throw new Error(error.message);
return data?.emailId;
}
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY!);
export async function POST(request: Request) {
const { to } = await request.json();
const { data, error } = await notix.emails.send({
from: "Acme <receipts@acme.com>",
to,
subject: "Your receipt",
html: "<p>Thanks for your order.</p>",
});
if (error) return Response.json({ error }, { status: 502 });
return Response.json({ id: data?.emailId });
}
The from address must be on a domain you have verified in Notix. Step by step: the Next.js quickstart and the longer guide to sending email from Next.js.
One API key, kept on the server.
Create an API key on the API keys page of the Notix console and store it as NOTIX_API_KEY in .env.local and your host's environment settings. Never give it the NEXT_PUBLIC_ prefix: variables with that prefix are built into the browser bundle, and anyone who opens the site can read them.
A Send only key is enough for code that only sends: if it leaks, it cannot read your contacts or delete a domain.
NOTIX_API_KEY=notix_xxxxxxxxxxxx
NOTIX_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx
Verify delivery events over the raw body.
Notix signs every webhook call with the webhook's signing secret: an HMAC-SHA256 of the X-Notix-Timestamp header, a dot and the raw body, sent in X-Notix-Signature. The SDKs check the signature and refuse a call more than 5 minutes old.
Read the body as text before parsing it: the signature covers the exact bytes, and JSON that is parsed and turned back into text can change them.
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY!);
const webhooks = notix.webhooks(process.env.NOTIX_WEBHOOK_SECRET!);
export async function handleNotixWebhook(request: Request): Promise<Response> {
const rawBody = await request.text();
try {
const event = webhooks.constructEvent(rawBody, { headers: request.headers });
if (event.type === "email.bounced") {
// Mark the address in your own records.
}
return new Response("ok");
} catch {
return new Response("invalid signature", { status: 400 });
}
}
Questions, answered.
Should I send from a server action or a route handler?
Does notix-js run on the Edge runtime?
What if the same request runs twice?
Why is my send refused with a from-address error?
Keep reading.
Send email from Next.js
A route handler, a server action and a contact form on the App Router, with a React Email template.
DocsNext.js
Send an email from a Next.js route handler or server action with notix-js.
IntegrationsIntegrations
Notix inside the frameworks, platforms and AI coding tools you already use, from Next.js and Laravel to Supabase, Vercel and Cursor.
ProductEmail API
One JSON API for transactional and marketing email: send, batch, schedule, webhooks, typed SDKs.
NotixPricing
A free plan with no card, and Pro at $15 a month for 50,000 emails. Only sent volume is metered.
Send your first email in five minutes.
Verify a domain, copy an API key, make one call. The free plan does not ask for a card.