Notix
Integrations

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.

The send

Send an email from Next.js.

app/actions/send-receipt.ts
"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;
}

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.

Environment

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.

.env
NOTIX_API_KEY=notix_xxxxxxxxxxxx
NOTIX_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx
Webhooks

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.

app/api/webhooks/notix/route.ts (export it as POST)
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 });
  }
}
FAQ

Questions, answered.

Should I send from a server action or a route handler?
Either works, because both run on the server. A server action suits a form in your own app; a route handler suits a call from another service or a webhook. Never send from a client component: the key would have to reach the browser.
Does notix-js run on the Edge runtime?
Keep the Node.js runtime, which is the default, for routes that import notix-js: the package imports Node's crypto module for webhook signatures. A route that must run at the edge can send with fetch to the API instead.
What if the same request runs twice?
Send an Idempotency-Key header (idempotencyKey in notix-js) with a value that names the thing being sent, such as order-4471-receipt. A repeat with the same key and body returns the first result and sends nothing new; the same key with a different body is refused with 409.
Why is my send refused with a from-address error?
The from address must be on a domain you have verified in Notix. Add the domain in the console, publish the DNS records it shows, and wait for verification; until then, sends from that domain are refused.

The free plan does not ask for a card.

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.