Send email from Next.js with an email API
Send email from Next.js in a route handler or a server action: one typed call, the key read on the server, an id back for every message.
// app/api/send/route.ts — npm install rasketimport { Rasket } from "rasket";
// A server action is the same three lines: the key// is read on the server, never in a client component.export async function POST(request: Request) { const apiKey = process.env.RASKET_API_KEY; if (apiKey === undefined) { return Response.json( { error: "not configured" }, { status: 500 }, ); }
const { orderId } = (await request.json()) as { orderId: string; };
const rasket = new Rasket({ apiKey, userAgent: "acme-billing/1.0", });
const { body } = await rasket.emails.send( { from: "Acme <orders@send.acme.example>", to: ["ronald.williams@example.com"], subject: "Your order has shipped", html: "<p>Order 1042 shipped today.</p>", }, { idempotencyKey: `order-${orderId}` }, );
return Response.json({ id: body.id });}What sending from Next.js actually involves
To send email from Next.js you send it from the server half of the application — a route handler, a server action or a server component — never from the browser, because the request carries an API key. The sample below is a route handler at app/api/send/route.ts; a server action reads the same environment variable and calls the same method.
Last updated 2026-09-16. Every field, header and limit below is the one the emails reference carries.
What you get with the key
Five things that arrive with the first request rather than with a later plan.
Idempotent sends
Put an Idempotency-Key on a send and repeat it as often as you like. For 24 hours the same key and payload return the first response instead of a second email, and a different payload under that key is refused rather than sent.
Signed webhooks you can replay
Delivery, bounce, complaint, open and click posted to your endpoint with a timestamp and a signature, retried on failure and replayable from the dashboard.
Inbound mail on your own domain
Receive at your domain on every plan. Each message arrives on a signed webhook with its headers, text and HTML, and its attachments behind signed links.
Templates with typed variables
Publish a template, then send it by ID or alias with the values it declares. Every email records the exact version it was rendered from.
Broadcasts and automations
Contacts with typed properties, topics people subscribe to, and per-contact workflows that wait, branch and send — through the same pipeline as the rest of your mail.
How to send email from Next.js
Six steps, in this order. Everything before the fifth is done once, and only the fifth is about the language you write in.
- 1
Create an API key
Open API keys in the dashboard and create one. It is shown once and stored hashed, so copy it then. Put it in your environment as RASKET_API_KEY and read it from there; a key committed to a repository is a key you have to rotate.
- 2
Add a sending domain
POST /domains with a domain you control. A subdomain such as send.acme.example is the usual choice, because it keeps this mail's reputation separate from the address your people write from. The response carries a records array.
- 3
Publish the DNS records
Every row of records goes into your DNS: the DKIM TXT record at rasket._domainkey, and the two records that give the domain its own return path. Nothing sends from the domain until they resolve.
- 4
Verify the domain
POST /domains/{domain_id}/verify. The domain comes back with a status on each record, and once it reads verified you can send from any address on it. An unverified sender is refused rather than quietly dropped.
- 5
Send your first email from Next.js
Post the message with the sample above. Carry an Idempotency-Key — yours or your queue's retry cannot then turn into a second email — and a User-Agent, which every request has to have. A 200 with an id means we have taken responsibility for the message.
- 6
Subscribe a webhook and check its signature
POST /webhooks with an https endpoint and the events you care about. Delivery, bounce, complaint, open and click arrive there with svix-id, svix-timestamp and svix-signature; verify against the raw body before anything parses it.
Verify webhooks in Next.js
In a route handler, call await request.text() and never await request.json(). Parsing an object and serialising it again is not the identity function — key order and whitespace both change — and the signature is over the bytes that arrived. Hand the text, request.headers and your whsec_ secret to verify() from @rasket/webhook-verify.
The five steps a verifier performs, in seven languages, are on the webhooks reference.
Read the long version
Sending email from Next.js: the full guide covers attachments, scheduling, suppressions and what to do when a message bounces.
Questions about Next.js
Do I need an SMTP server to send email from Next.js?
No. The send is an HTTPS request from your server code, so it works the same on a Node server, on a serverless function and at the edge. There is no SMTP relay in Rasket, and nothing to configure beyond a key and a verified domain.
Where do I keep the API key in a Next.js app?
In the environment as RASKET_API_KEY, with no NEXT_PUBLIC_ prefix — that prefix is what puts a value in the browser bundle, and a key in a browser bundle is a key anyone can read. Read it inside the route handler or server action, never in a client component.
Can I send from a server action instead of a route handler?
Yes, and it is the same three lines: read the key, construct the client, await the send. Use a route handler when something outside your app has to trigger the mail — a queue, a cron, another service — and a server action when a form submission does.
How do I stop a retry from sending the same email twice?
Give the send an idempotencyKey derived from the thing it is about, such as the order id, rather than a fresh random string. A serverless function that times out and is retried then repeats the same key, and for 24 hours that returns the first response instead of sending a second email.
How do I verify a Rasket webhook in Next.js?
Add a route handler, read await request.text() as the very first thing it does, and pass that text with request.headers to verify() from @rasket/webhook-verify. Answer 200 once you have stored the event and 400 when verification throws; retries are automatic and replays are available from the dashboard.
Keep reading
Make your first send
Create a key, verify a domain, and post your first message from Next.js. The free plan does not expire.