Next.js email guide
How to send email from Next.js
Send a transactional email from Next.js with the RouteKite REST API while keeping the API key in server-only code.
Reviewed September 6, 2026
Keep the rk_ API key outside browser components and public environment variables.
Send HTML, plain text, recipient, subject, and sender to the email endpoint.
Use the accepted message record to retrieve its current state and delivery history.
Setup
Prepare the server environment
You need a verified domain, a Sending Address, and an rk_ API key. The example uses a server-only module, so the credential never enters the client bundle.
- Verify your sending domain
Add the DNS values shown by RouteKite and wait until the domain is ready.
- Create a Sending Address
Use an address on the verified domain, such as orders@yourdomain.com.
- Create an API key
Store it in the Next.js server environment and never prefix it with NEXT_PUBLIC_.
ROUTEKITE_API_KEY=rk_your_keyImplementation
Send the email
Place this helper in server-owned application code and call it after the product event that requires the email.
import "server-only";
type OrderConfirmation = {
recipient: string;
orderNumber: string;
};
export async function sendOrderConfirmation({
recipient,
orderNumber,
}: OrderConfirmation) {
const apiKey = process.env.ROUTEKITE_API_KEY;
if (!apiKey) {
throw new Error("ROUTEKITE_API_KEY is not configured");
}
const response = await fetch("https://routekite.com/api/v1/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${orderNumber}-confirmation`,
},
body: JSON.stringify({
from: "orders@yourdomain.com",
to: recipient,
subject: `Order ${orderNumber} confirmed`,
preview_text: "We received your order.",
html: `<h1>Order confirmed</h1><p>Order ${orderNumber} is being prepared.</p>`,
text: `Order ${orderNumber} is confirmed and being prepared.`,
}),
cache: "no-store",
});
const message = await response.json();
if (!response.ok) {
throw new Error(message.message ?? "RouteKite could not accept the email");
}
return message;
}The idempotency key identifies this exact order-confirmation request. Retrying the same payload with the same key returns the original message instead of creating a second send.
Response
Store the returned message ID
A successful request creates a RouteKite message record. Queued means the request was accepted and prepared for delivery.
{
"id": "7dd5da7f-40b0-4c29-bb78-7e0d95ef9eb7",
"status": "queued",
"created_at": "2026-09-04T14:32:18.000Z"
}Use the message ID with the retrieve-email and delivery-history endpoints. A queued or submitted state is not a delivered result, so read the later provider-reported state when delivery matters.
Production checklist
Before connecting a product event
Keep the sending boundary small and predictable so product retries do not create duplicate customer messages.
- Protect the key
- Read it only from server-side environment configuration.
- Use stable idempotency
- Derive the key from the business event rather than generating a new key for every retry.
- Handle non-2xx responses
- Log a safe internal reference and preserve the original product action.
- Track the message ID
- Use it to retrieve the current state and delivery history later.
RouteKite API
Send the first message from your Next.js server.
Review the complete request contract, error model, attachments, and retrieval endpoints in Docs.