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

StepTime (approx)
1. Create Notify account + API key5 min
2. Add domain, copy SPF/DKIM5 min (DNS propagation)
3. Swap send helper in code5 min
4. Point staging webhooks5 min
5. Replay reset + receipt flows5 min
6. Dual-run / cutover5 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

ConceptResendSendGridTypical SMTP/NodemailerNotify
AuthAuthorization: BearerAuthorization: Beareruser/pass on relayx-api-key
Endpointapi.resend.com/emailsapi.sendgrid.com/v3/mail/sendSMTP hostnotify.cx/api/email/send
Body HTMLhtmlcontent[].valuehtmlmessage
Body texttextcontent[]textmessage

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

  1. Export your current suppression list (hard bounces)
  2. Create Notify webhooks on Pro/Scale (webhooks docs)
  3. Keep the old provider’s webhooks live for a week while draining in-flight retries
  4. Make sends idempotent in your DB so dual-run doesn’t double-email (especially Stripe receipts)

Cutover strategy

  1. Staging first — full auth + billing email suite on Notify
  2. Shadow — log Notify payloads without sending (optional)
  3. Percent or feature-flag — new signups on Notify
  4. Hard cut — flip env vars (NOTIFY_API_KEY, remove old SDK)
  5. 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
1

Sign in

Google or GitHub

2

Complete profile

Takes a few minutes

3

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…

Posts Across the Network