Skip to content
GuideRun end to end with Next.js 16.4, 3 min read

Send email from Next.js with Mailbay

A route handler and two environment variables. Keep the Resend SDK and change its base URL, or call the API with fetch. We ran all three against Mailbay.

.env.local
RESEND_BASE_URL=https://api.mailbay.dev/v1
RESEND_API_KEY=mb_live_xxxxxxxxx   # a Mailbay Sending only key
Terminal showing a Next.js app's .env.local with masked keys, two POST requests to route handlers that return email ids, and both emails delivered
A Next.js 16.4 app with two route handlers, one using the Resend SDK pointed at Mailbay and one using the mailbay SDK. Both sends returned an id and were delivered. Local stack; keys masked.

Set up in one paste

If your app already sends with the Resend SDK, step 3 is the whole migration.

  1. 1

    Verify your domain

    The from address must be on a domain verified in your Mailbay project. Records and checks are in the domains docs.
  2. 2

    Create a Sending only key

    It can send and read the send log, and nothing else. Keep it in server env vars.
  3. 3

    Set two env vars

    RESEND_BASE_URL and RESEND_API_KEY in .env.local and in your host's settings. Never with a NEXT_PUBLIC_ prefix.

The route handler

Three ways to make the same call. All three ran against Mailbay and returned an email id.

import { Resend } from "resend";

const resend = new Resend(process.env.RESEND_API_KEY); // reads RESEND_BASE_URL

export async function POST(request: Request) {
  const { email } = await request.json();
  const { data, error } = await resend.emails.send({
    from: "Acme <noreply@acme.com>",
    to: [email],
    subject: "Welcome to Acme",
    html: "<p>Thanks for signing up.</p>",
  });
  if (error) return Response.json({ error }, { status: 400 });
  return Response.json(data); // { id: "eml_..." }
}

Tested with resend 6.32.1.

The full request and response, scheduling and batches are in the Emails API docs; the SDK's methods and errors are in the Node SDK docs.

What you can do from Next.js

  • POST /v1/emails

    Welcome and receipt emails

    From your own domain, with html, text, attachments and tags.
  • Schedule and batch

    Send later with scheduled_at, or up to 100 emails in one request.
  • Auth emails from Supabase

    Using Supabase Auth? Point it at Mailbay with custom SMTP, no code needed.
  • Retries that never double send

    The same Idempotency-Key within 24 hours returns the first result.
  • Know what happened

    Read a send's status, or get message.* webhooks for delivered and bounced.

A real run

On October 14, 2026 we created a Next.js 16.4.0 app with these route handlers, set .env.local to a local Mailbay stack, and posted to each one. Each returned an email id, and Mailbay's API reported both emails delivered: the capture at the top of this page. Before sending to real people from your domain, send one message to the email deliverability test.

If a send fails

403 sender_not_verified
The from address is not on a verified domain of the key's project. The error's fix field names the commands.
401 invalid_api_key
The key is missing or wrong. Check the variable name, and restart next dev after editing .env.local.
400 missing_body
Add text or html, or both. Every error says what to change.
The SDK still calls Resend
RESEND_BASE_URL is not set where the code runs. Moving from Resend? See the Resend alternative page.

Shipping a SaaS on Next.js? Mailbay for SaaS teams covers support inboxes and webhooks too. All other frameworks are in the integrations directory.

Questions

Do I have to stop using the Resend SDK?

No. Mailbay's POST /v1/emails takes Resend's request body, and the resend package reads its base URL from RESEND_BASE_URL. Change that and the key; the code stays. We ran resend 6.32.1 this way.

Is the mailbay package on npm?

Not yet. The SDK is written, documented and tested, and it is being published to npm. Until then use the Resend SDK or fetch, which call the same endpoint.

Can I send from a server action instead of a route handler?

Yes. The same call works in any server code: a server action, a route handler or middleware that runs on the server. Never call it from a client component, because the key must stay on the server.

Does it work on the edge runtime?

The fetch version uses only the global fetch, and the mailbay SDK uses fetch and Web Crypto only, so both run on edge runtimes. Our run used the Node.js runtime.

Join the beta

Mailbay is in private beta and free while the beta runs: inboxes, sending, your own domains and MCP. We invite people in small batches and read every note on the form.