Frameworks

Next.js

Send transactional emails from Next.js in five minutes — no SDK, one fetch.

Send transactional emails from your Next.js app in five minutes. No SDK required — the whole integration is one fetch.

1. Set up environment variables

Add to .env.local:

EMITD_API_KEY=esk_live_...

2. Create a route handler

app/api/send-email/route.ts
export async function POST(request: Request) {
  const { email, name } = await request.json();

  const res = await fetch("https://api.emitd.com/v1/email", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.EMITD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      from: "hello@yourdomain.com",
      to: [email],
      subject: `Welcome, ${name}!`,
      html_body: `
        <h1>Welcome to our app!</h1>
        <p>Hi ${name},</p>
        <p>Thanks for signing up. We're excited to have you.</p>
      `,
      tags: { campaign: "welcome" },
    }),
  });

  return Response.json(await res.json());
}

Never call emitd from the browser

Keep EMITD_API_KEY in a route handler or server action — the App Router makes that the default. A key shipped to the client is a leaked key.

3. Use it in a component

app/page.tsx
"use client";

import { useState } from "react";

export default function Home() {
  const [email, setEmail] = useState("");
  const [name, setName] = useState("");
  const [status, setStatus] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setStatus("sending...");

    const res = await fetch("/api/send-email", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, name }),
    });

    const data = await res.json();
    setStatus(data.status === "queued" ? "Sent!" : "Error");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input placeholder="Name" value={name} onChange={(e) => setName(e.target.value)} />
      <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />
      <button type="submit">Send welcome email</button>
      <p>{status}</p>
    </form>
  );
}

4. Verify your domain

Before sending from production, verify your domain in the console: add it, publish the DNS records (SPF/DKIM/DMARC), and wait for verification (usually under five minutes). See Sending domains.

5. Test with sandbox recipients

For development, flip test mode on and send to a reserved recipient — no real email leaves the building, every event still fires:

body: JSON.stringify({
  from: "hello@yourdomain.com",
  to: ["delivered@relay.dev"],
  subject: "Test email",
  html_body: "<p>This is a test</p>",
  test_mode: true,
}),

bounced@relay.dev and complained@relay.dev simulate the other outcomes; inspect results at GET /v1/email/test-events. See Test mode.

What's next?

  • Templates — server-side rendering with template_alias + template_model
  • Webhooks — HMAC-signed delivery events
  • Idempotency — safe retries with Idempotency-Key
  • Scheduling — scheduled_at in epoch ms

On this page