Switching Transactional Email Providers With Zero Downtime
Moving your app's email to a new provider doesn't need a risky big-bang cut-over. Dual DKIM, a thin sending interface, gradual rollout and suppression export.

On this page(10 sections)
- Why teams switch
- Step 1: Put a thin interface in front of sending
- Step 2: Authenticate the new provider alongside the old one
- Step 3: Move your suppression list
- Step 4: Roll out gradually
- Step 5: Handle webhooks from both providers
- Step 6: Watch the right numbers
- Step 7: Finish the switch
- A note on SMTP
- Key takeaways
Switching the service your app uses to send password resets and receipts sounds like a weekend job: change an API key, swap a client library, deploy. Then the first customer can't log in because their reset email went to spam, and you discover that nobody exported the suppression list.
A provider switch can be boring and safe if you treat it as a gradual rollout rather than a single cut-over. Here's the sequence.
Why teams switch
The reasons vary: cost at a new volume, a feature you need (inbound handling, better logs, replies landing in a team inbox), support quality, consolidating vendors, or deliverability problems you've traced to the provider. The reason affects what you test, but the mechanics are the same.
Step 1: Put a thin interface in front of sending
If provider-specific code is scattered across your app, start here. Create one internal function or module that every part of the app calls to send email, and make it the only place that knows which provider exists.
// email/send.ts
export interface OutboundEmail {
from: string;
to: string;
subject: string;
html: string;
text?: string;
tag?: string; // e.g. "password-reset"
}
export async function sendEmail(msg: OutboundEmail) {
const provider = pickProvider(msg); // old or new, see step 4
return provider.send(msg);
}
This is worth doing even if you never switch again. It makes testing easier, gives you one place to add logging, and keeps future changes small.
Step 2: Authenticate the new provider alongside the old one
Your domain's DNS can authorize both providers at once. Do this days before any mail moves.
- DKIM: each provider signs with its own key under its own selector, so both can be published at the same time. Add the new provider's DKIM record without touching the old one.
- SPF: add the new provider's include to your existing SPF record. Watch the limit of ten DNS lookups; if you're close, remove includes for services you no longer use.
- Return-path or bounce domain: some providers ask for a CNAME on a subdomain to handle bounces and align SPF. Set it up now.
- DMARC: no change needed if both providers pass DKIM with your domain aligned. Your DMARC aggregate reports will show the new provider's traffic once it starts.
Verify with the new provider's dashboard and a DMARC check before sending anything real.
Step 3: Move your suppression list
This is the step teams skip and regret. Your old provider has a list of addresses that hard-bounced, complained or unsubscribed. If you don't carry it over, the new provider will happily send to all of them, your bounce and complaint rates will spike, and your new sender reputation starts off damaged.
- Export hard bounces, spam complaints and any unsubscribes from the old provider.
- Import them into the new provider's suppression list, or into your own database if you keep suppression in-app.
- Decide how you'll keep both in sync during the overlap. The simplest option is to handle bounce and complaint webhooks from both providers and write to one shared table in your app.
Step 4: Roll out gradually
Route a small share of mail to the new provider first, then increase it. Two common approaches:
By message type. Start with low-risk, low-volume email, such as internal notifications or weekly digests, then move onboarding email, and move password resets and receipts last.
By percentage. Send a fixed percentage of all mail through the new provider using a feature flag, and raise it as metrics hold.
function pickProvider(msg: OutboundEmail) {
const rollout = Number(process.env.NEW_PROVIDER_PERCENT ?? 0);
// Stable per-recipient bucketing so one person doesn't flip between providers
const bucket = hash(msg.to) % 100;
return bucket < rollout ? newProvider : oldProvider;
}
A sensible ramp is a few days at each step, such as 5%, 25%, 50% and 100%, watching delivery at each stage. If you're also sending from a newly authenticated domain, ramp more slowly; new sending identities build reputation over time.
Step 5: Handle webhooks from both providers
During the overlap, delivery, bounce and complaint events arrive from two places with two formats. Normalize them into one internal event shape before your app acts on them.
- Give each provider its own webhook endpoint.
- Verify each provider's webhook signature.
- Map their event names to yours: delivered, bounced, complained, opened, clicked.
- Make handlers idempotent; webhooks can be delivered more than once.
Step 6: Watch the right numbers
| Metric | Where to look | What a problem looks like |
|---|---|---|
| Bounce rate | Both providers' dashboards | New provider noticeably higher than old for the same mail |
| Complaint rate | Webhooks, feedback loops | Any increase after moving a message type |
| Inbox placement | Seed accounts at major mailbox providers | Password resets landing in spam |
| Delivery latency | Timestamp of send vs receipt | Resets taking minutes instead of seconds |
| Support tickets | Your support inbox | "I never got the email" rising |
Keep a small set of test accounts at Gmail, Outlook, Yahoo and a couple of business domains, and send each critical template to them at each rollout step.
Step 7: Finish the switch
Once 100% of mail has gone through the new provider for a week or two without problems:
- Remove the old provider's SPF include.
- Leave its DKIM record for a few more weeks so delayed or retried mail still verifies, then remove it.
- Remove old webhook endpoints and rotate or delete old API keys and SMTP credentials.
- Update your runbooks and the email inventory.
- Close the old account only after you've saved any logs you might need.
A note on SMTP
If parts of your stack send over SMTP rather than an API, such as a legacy app, a CMS or a billing tool, those need moving too. Most providers offer an SMTP relay; switching is usually a matter of changing host, port and credentials. Include each SMTP sender in your rollout plan and test it separately.
If Koltrix is your destination, the REST endpoint is POST https://api.koltrix.com/api/v2/emails with an API key, and anything that already speaks SMTP can point at the relay instead. The sending docs cover both, and the webhooks reference covers delivery events.
Key takeaways
- Wrap sending behind one internal interface before you switch.
- Publish the new provider's DKIM and SPF alongside the old ones days in advance.
- Export and import your suppression list; never start fresh.
- Roll out by message type or percentage, with password resets last.
- Normalize webhooks from both providers, watch bounces and placement, and clean up DNS and credentials at the end.
Start with Koltrix
Your domain, one inbox, and an API that sends.
A team inbox where AI sorts and drafts (nothing is sent without your click), plus the transactional API and SMTP relay your product sends with. 7 days free, no card.
