Migrate Off Your Email Provider in Thirty Minutes (Resend, SendGrid, SES, SMTP)
Switching transactional email providers is mostly mechanical: DNS, auth header, JSON field names, webhooks. The scary part is reputation and duplicate…
3 min readElena Voss
Switching transactional email providers is mostly mechanical: DNS, auth header, JSON field names, webhooks. The scary part is reputation and duplicate sends — not the code diff.
This guide uses Notify as the destination (POST https://notify.cx/api/email/send, x-api-key, message field). The checklist works for any move; only the before/after snippets change.
Migrate when the job fits: transactional send + logs + webhooks for password resets, onboarding, receipts, and app notifications. Don’t migrate if you need a marketing suite, newsletter tooling, or a drag-and-drop template studio in the same product — Notify is deliberately not that.
The thirty-minute checklist
| Step | Time (approx) |
|---|---|
| 1. Create Notify account + API key | 5 min |
| 2. Add domain, copy SPF/DKIM | 5 min (DNS propagation) |
| 3. Swap send helper in code | 5 min |
| 4. Point staging webhooks | 5 min |
| 5. Replay reset + receipt flows | 5 min |
| 6. Dual-run / cutover | 5 min + watch period |
DNS is usually the long pole. Start domain verification first. Grab a key from credentials. Pricing reference: Free 1,000 emails/mo, Pro $10 / 10,000, Scale $50 / 100,000 — pricing.
Field mapping
| Concept | Resend | SendGrid | Typical SMTP/Nodemailer | Notify |
|---|---|---|---|---|
| Auth | Authorization: Bearer | Authorization: Bearer | user/pass on relay | x-api-key |
| Endpoint | api.resend.com/emails | api.sendgrid.com/v3/mail/send | SMTP host | notify.cx/api/email/send |
| Body HTML | html | content[].value | html | message |
| Body text | text | content[] | text | message |
Notify uses one message field for plain text or HTML. Until the new domain is verified, rehearse with POST https://notify.cx/api/email/send/test — sandbox vs production.
Shared Notify helper (after)
Centralize the new client once so every call site flips together:
// lib/notify.ts
export async function sendEmail(opts: {
to: string;
subject: string;
message: string;
from?: string;
}) {
const res = await fetch('https://notify.cx/api/email/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.NOTIFY_API_KEY!
},
body: JSON.stringify({
from: opts.from ?? 'noreply@your-verified-domain.com',
to: opts.to,
subject: opts.subject,
message: opts.message
})
});
if (!res.ok) throw new Error(await res.text());
return res.json();
}
Before / after — Resend
// Before (Resend)
await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
from: 'Acme <noreply@acme.com>',
to: 'user@example.com',
subject: 'Welcome',
html: '<h1>Welcome</h1>'
})
});
// After (Notify)
await fetch('https://notify.cx/api/email/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.NOTIFY_API_KEY
},
body: JSON.stringify({
from: 'noreply@your-verified-domain.com',
to: 'user@example.com',
subject: 'Welcome',
message: '<h1>Welcome</h1>'
})
});
Full walkthrough: Migrate from Resend · Compare Resend
Before / after — SendGrid
// Before (shape)
await fetch('https://api.sendgrid.com/v3/mail/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
personalizations: [{ to: [{ email: 'user@example.com' }] }],
from: { email: 'noreply@acme.com' },
subject: 'Welcome',
content: [{ type: 'text/html', value: '<h1>Welcome</h1>' }]
})
});
// After — same Notify payload as the Resend example above
Docs: Migrate from SendGrid · Compare SendGrid
Mailgun is a similar mechanical remap — Migrate from Mailgun.
From Amazon SES
Replace the SES SDK call with the Notify fetch. Keep your domain’s DNS updated for the new provider’s DKIM. Expect to re-verify the domain on Notify even if SES already knew it.
Docs: Migrate from SES · Compare SES
Why teams leave SES early: sandbox waits and DIY bounce plumbing — SES blog.
From Nodemailer / raw SMTP
// Before
await transporter.sendMail({
from: 'noreply@acme.com',
to: 'user@example.com',
subject: 'Welcome',
html: '<h1>Welcome</h1>'
});
// After
await fetch('https://notify.cx/api/email/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.NOTIFY_API_KEY
},
body: JSON.stringify({
from: 'noreply@your-verified-domain.com',
to: 'user@example.com',
subject: 'Welcome',
message: '<h1>Welcome</h1>'
})
});
Docs: Migrate from SMTP
Webhooks and suppressions
- Export your current suppression list (hard bounces)
- Create Notify webhooks on Pro/Scale (webhooks docs)
- Keep the old provider’s webhooks live for a week while draining in-flight retries
- Make sends idempotent in your DB so dual-run doesn’t double-email (especially Stripe receipts)
Cutover strategy
- Staging first — full auth + billing email suite on Notify
- Shadow — log Notify payloads without sending (optional)
- Percent or feature-flag — new signups on Notify
- Hard cut — flip env vars (
NOTIFY_API_KEY, remove old SDK) - Watch bounce rates and support tickets 48–72h
When not to migrate
- You depend on SendGrid (or similar) marketing campaigns in the same account
- You need inbound parse as a first-class product (evaluate Postmark/Mailgun carefully)
- You’re mid-incident and changing ESP adds risk — stabilize first
Bottom line
A transactional migration is a field rename, a DNS update, and webhook hygiene. Notify’s contract is small on purpose so the diff stays small. Start with the quick start, then cut over one helper.
Resources
More in software-development
Venture
Write for entrepreneurs, founders, and builders.
Share startup lessons, growth tactics, and founder stories with readers on the same journey.
One free account across In Plain English, Stackademic, Venture, and Cubed.
How it works- Startups & entrepreneurship
- Marketing & growth
- Productivity & leadership
- Founder stories & lessons learned
Sign in
Google or GitHub
Complete profile
Takes a few minutes
Get approved & publish
Start sharing
Why write for Venture?
Entrepreneurship is rarely a straight path. The lessons worth sharing are learned while building.
Comments
Loading comments…