Migration guide
Migrate from Resend to Volanea.
Resend and Volanea are close cousins on the transactional side, so this is one of the easiest migrations — the send call maps almost one-to-one, and even the webhook event names largely match.
Undecided? Read the honest Volanea vs Resend comparison first.
Create the project and verify your domain
Sign up (free, no card), add your sending domain, and install the DKIM/SPF records Volanea shows you — fresh records, separate from Resend's. Keep Resend live for now: nothing gaps while DNS propagates, and verification is one click once it does.
Swap the send call
This is the heart of it — one call replaced by one call. Grab your secret key from project settings first.
before — resend
import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: "shop@yourdomain.com",
to: "ada@example.com",
subject: "Your order shipped",
html: "<p>On its way, Ada.</p>",
});after — volanea
// npm i @volanea/sdk (or plain fetch — it's one POST)
import { Volanea } from "@volanea/sdk";
const volanea = new Volanea({ apiKey: process.env.VOLANEA_SECRET_KEY! });
await volanea.send(
{
to: "ada@example.com",
subject: "Your order shipped",
html: "<p>On its way, {{firstName}}.</p>",
variables: { firstName: "Ada" },
},
{ idempotencyKey: "order-4921" }, // retries become safe replays
);- from is optional on Volanea — it defaults to your verified domain's sender. Pass it only when you need a specific address.
- Variables with {{placeholders}} are rendered server-side — no need to interpolate strings before sending.
- The response returns as soon as the message is durably queued (status: "queued"); final state arrives via webhooks or GET /v1/emails/{id}.
Bring your contacts
Export your list from Resend as CSV and import it in the Volanea dashboard (or POST /v1/contacts/import). Columns map by header, imports run as a background job with live progress, and re-runs dedupe by email — a partial import is never a disaster. Unsubscribed contacts keep their status, so nobody who opted out gets mailed by mistake.
Re-point your webhooks
Register your endpoint in Volanea (the response includes the whsec_… signing secret), then translate the event names your handler listens for:
| Resend | Volanea |
|---|---|
| email.sent | email.sent |
| email.delivered | email.delivered |
| email.opened | email.opened |
| email.clicked | email.clicked |
| email.bounced | email.bounced |
| email.complained | email.complained |
Every delivery is HMAC-signed in the X-Volanea-Signature header and retried with backoff for ~24 hours — the docs include a drop-in verifier.
Test, then flip
Send through your test key (sk_test_…) first — messages render and log fully but deliver nothing, so you can assert on the whole pipeline in CI. When the domain is verified and the test sends look right, swap in the live key, watch the first real sends land in the dashboard, and retire the Resend credentials.
Watch for the gotchas
- React Email users: render to HTML in your own code (render() from @react-email/render) and pass the result as html — there's no first-party React integration.
- Resend's Audiences don't export broadcast history — export contacts as CSV before you close the account if you want the list.
- Domain verification is fresh DNS: new DKIM/SPF records from Volanea's dashboard, verified in one click once DNS propagates.