Migrate from Resend
Drop-in compatible. Change one import. Keep your code, your webhooks, your retry logic, your idempotency keys, your error-handling patterns. Most projects migrate in under 5 minutes; the rest of this page covers the edge cases.
The 5-minute migration
1. Install the SDK
npm install @mailstorm/resend
# or pip install mailstorm-resend2. Run the codemod
npx mailstorm migrate # dry run, prints what would change npx mailstorm migrate --apply # actually rewrites imports
What it does:
- Walks
.ts/.tsx/.js/.jsx/.mjs/.cjsin your repo - Rewrites
import ... from "resend"→"@mailstorm/resend" - Rewrites
require("resend")→"@mailstorm/resend" - Rewrites
await import("resend")→"@mailstorm/resend" - Skips
node_modules,.git,dist,build,.next,.svelte-kit,.turbo,.vercel,.output,coverage,.cache - Reports
RESEND_API_KEYmentions in.env*files (does not auto-rewrite — that's a deliberate developer decision)
3. Manual step: API key
The codemod won't change your API key — that's intentional. You decide between two paths:
Or keep both during cutover and use a different env var name in code:
const resend = new Resend(process.env.MAILSTORM_API_KEY ?? process.env.RESEND_API_KEY!);
Generate a Mailstorm key at /api-keys. Test mode (ms_test_*) captures without sending — use it to verify your integration before flipping to live.
4. Verify your sending domain
Mailstorm requires DNS verification before live sends. Add the domain at /domains. The DKIM, SPF, and DMARC records are familiar — paste them in your DNS host (Vercel DNS, Cloudflare, Route 53, etc.). Verification polls automatically; typically green within 5 minutes.
If your existing Resend domain has DMARC at p=quarantine or p=reject: our DKIM signs with a different selector (mailstorm), so you can keep both Resend and Mailstorm DKIM records active during cutover. Both pass DMARC, no domain reputation lost.
5. Reconfigure webhooks
Create webhooks at /webhooks pointing at the same endpoint URL you used with Resend. Same event names. Same JSON shape. Only header differs:
Both are HMAC-SHA256 of the raw body using the secret you configured. Verification logic is byte-identical.
Compatibility matrix (v0.1)
Email API
| Resend method | Status | Notes |
|---|---|---|
resend.emails.send() | ✅ Drop-in | Identical request & response shapes |
resend.emails.create() | ✅ Drop-in | Alias of send |
resend.emails.get(id) | ✅ Drop-in | Returns object, last_event, to as array (Resend shape) plus our native fields |
resend.emails.update(id) | ○ Stub | Schedule-send not yet implemented; returns clear error |
resend.emails.cancel(id) | ○ Stub | Same — schedule-send is post-Phase 7 |
resend.batch.send() | ✅ Drop-in | Up to 100 emails; {data: {data: [{id}]}} response |
resend.batch.create() | ✅ Drop-in | Alias |
Domains
| Resend method | Status |
|---|---|
resend.domains.create/list/get/verify/remove/update | ~ Use @mailstorm/sdk for now (or the dashboard) |
Coverage in @mailstorm/resend is a roadmap item; for v0.1, manage domains via the dashboard or our native SDK.
Audiences / Contacts / Broadcasts / API Keys
| Resend method | Status |
|---|---|
resend.audiences.* | ~ Native API at /api/contact-lists |
resend.contacts.* | ~ Native API at /api/contacts |
resend.broadcasts.* | ~ Native API at /api/broadcasts |
resend.apiKeys.* | Use the dashboard at /api-keys |
Webhook events
| Resend event | Mailstorm event | Status |
|---|---|---|
email.sent | email.sent | ✅ |
email.delivered | email.delivered | ✅ |
email.delivery_delayed | email.delivery_delayed | ~ Coming Phase 5 |
email.complained | email.complained | ✅ |
email.bounced | email.bounced | ✅ |
email.opened | email.opened | ✅ |
email.clicked | email.clicked | ✅ |
email.received | email.received | ✅ + AI fields included |
Things that work different on purpose
Pricing model
Resend bills transactional and marketing as separate plans. Mailstorm bundles both into one tier — your monthly allowance covers all sends, no SKU split. See /pricing.
FROM-domain enforcement
Live sends require a verified FROM domain. Resend allows sending from onboarding@resend.dev without owning a domain. We mirror this with test mode — ms_test_* keys send from any domain — but live sends require ownership. This was added 2026-04-26 after a phishing operation exploited the gap.
Per-tier daily caps + new-account probation
A new account is capped at 100 sends/day for the first 7 days regardless of tier. After probation, the monthly cap takes over (free 5k/mo, hobby 50k/mo, pro 100k/mo). On Free there's also a 200/day hard cap; Hobby and Pro have no daily limit beyond your plan's monthly allotment. This is a brand-protection trade-off that makes our IP reputation more durable.
AI inbound fields
Mailstorm's email.received webhook payload includes structured AI fields (category, urgency, language, confidence) classified before the webhook fires. Resend's payload is metadata only and forces you to fetch the body via a second API call. The Mailstorm shape is a superset — your existing Resend handler reads only the fields it knows; the AI fields are additive.
Migration audit checklist
Before flipping production traffic, verify:
- [ ] All
resendimports rewritten (npx mailstorm migrate) - [ ]
MAILSTORM_API_KEYset in every environment (Production / Preview / Development) - [ ] Sending domain verified (DKIM / SPF / DMARC green at
/domains) - [ ] Webhook URL re-registered at
/webhooks - [ ] HMAC verification reading
X-Mailstorm-Signature(replaceResend-Signature) - [ ] Test mode (
ms_test_*) sends pass your integration tests - [ ] Live mode test send to your own inbox confirms inbox placement (not Spam)
- [ ] Headers
SPF: PASS,DKIM: PASS,DMARC: PASSin the test message (Gmail → ⋮ → Show Original) - [ ] Optional: keep Resend installed for 1 week as fallback while you watch Mailstorm logs/metrics
Rollback
If you need to roll back during the cutover window, the inverse migration is the same one-liner:
Flip your env var back to RESEND_API_KEY, swap the webhook URL back, and you're on Resend again. We design for migration to be reversible — that's the only way it's actually safe.
Get help
Stuck on something specific? Email support@mailstorm.dev with your migration question. We respond within 24 business hours (LATAM time zone). For urgent migration help during a cutover, mention "migration assist" in the subject and we prioritize.