Guides/The Developer's Guide to Transactional Email/Operating Transactional Email at Scale

Chapter 4

Operating Transactional Email at Scale

The operational concerns that separate a prototype from production: making sends reliable with retries and idempotency, the metrics that matter, knowing when email alone isn't enough, and deciding whether to build or buy.

Charcoal guide cover with aurora gradient panel and Courier mark.

Last updated: June 2026

Sending one email is easy; running a transactional email system that's reliable, observable, and ready to scale is the part that earns its keep. This chapter covers the operational concerns that separate a prototype from production: making sends reliable, measuring what matters, knowing when email alone isn't enough, and deciding whether to build or buy.

How to make sends reliable: retries and idempotency

In production, email sends fail intermittently: a provider has a blip, a network request times out, your process crashes mid-send. Reliability means handling those failures without losing email and without sending duplicates, and the two tools for that are retries and idempotency.

Retries handle transient failures. When a send fails with a temporary error, you try again, ideally with exponential backoff (wait 1 second, then 2, then 4) so you don't hammer a struggling provider. A durable queue in front of your sends makes this robust: enqueue the send, process it with a worker, and let the queue redeliver the job if the worker dies.

Idempotency keeps retries from causing duplicates. If your worker sends an email and then crashes before recording success, a naive retry sends the email twice, and now your user has two password reset codes and no idea which is real. An idempotency key solves this: you attach a unique key to each logical send, and the provider (or your own dedup layer) ignores a second request with the same key.

Copied!

async function sendWithRetry(message, idempotencyKey, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
// The idempotency key ensures a retried request never sends twice
return await emailProvider.send(message, { idempotencyKey });
} catch (error) {
if (!error.isTransient || attempt === maxAttempts) {
throw error;
}
const backoffMs = 2 ** (attempt - 1) * 1000;
await new Promise((resolve) => setTimeout(resolve, backoffMs));
}
}
}

Derive the idempotency key from something stable about the event, like password-reset:${userId}:${requestId}, rather than generating a random one per attempt. That way every retry of the same logical send carries the same key, and the duplicate is caught.

Key takeaway: Retries without idempotency are worse than no retries. A random key per attempt defeats the whole mechanism. Always derive the key from the event, not the attempt.

How to keep transactional email secure

Transactional email often carries the keys to an account: reset links, login codes, verification tokens. That makes it a security surface, not only a delivery problem, and a few rules keep it from becoming a liability.

  • Expire and single-use your tokens. A reset or magic link should be valid for minutes, not days, and should stop working after one use. A reset link that lives forever in an inbox is a standing account-takeover risk.
  • Avoid open redirects. If your links route through a redirect or click-tracking URL, validate the destination. An unvalidated redirect in a trusted email is a phishing gift.
  • Keep sensitive data out of the body. Email isn't a secure channel and ends up in logs, backups, and previews. Send a link to the receipt or the data, not full card numbers, government IDs, or complete account details.
  • Don't leak through enumeration. A "we've sent a reset link if that account exists" message keeps attackers from probing which addresses are registered.
  • Authenticate to prevent spoofing. SPF, DKIM, and DMARC (chapter 3) aren't only deliverability tools. DMARC at enforcement is what stops someone sending account-takeover phishing from your own domain.

None of this is exotic, but it's the part teams skip when they treat transactional email as plumbing. The messages that matter most for reliability are usually the same ones that matter most for security.

Which transactional email metrics to track

You can't operate what you can't see, so instrument your transactional email from day one. The metrics that matter fall into five groups, and most ESPs expose the first four through dashboards and webhooks. The fifth, business impact, you have to connect yourself, and it's the one that tells you whether the email is doing its job.

CategoryKey metricsHealthy target
DeliveryDelivered rate, bounce rate, rejection rateDelivered 98-99%+, hard bounce <0.5%, soft bounce <1.5%
DeliverabilityInbox placement, spam-folder rate, spam-complaint rateSpam complaints <0.3% (ideally <0.1%)
EngagementOpen rate, click rate, time to openDirectional; investigate sudden drops
Technical healthSend latency, API error rate, queue depth, retry rateTime-critical sends in seconds, low error rate
Business impactAction-completion rate, support deflection, revenue influencedTrending up over time

Delivery and deliverability are your reputation early-warning system. Keep your delivered rate at 98-99% or higher; a drop usually points to authentication, list-hygiene, or blocklist problems. Keep hard bounces under about 0.5%, and keep your spam-complaint rate below 0.3% (and ideally under 0.1%), because crossing Gmail's threshold throttles your delivery fast. Inbox placement is the metric that actually matters: a 99% delivered rate means nothing if half of it lands in spam.

Engagement matters less for transactional email than for marketing email, but it's not noise. A sudden drop in open rate is often the first sign you've started landing in spam, before the complaint numbers catch up.

Technical health is the part only you can see, so set alerts on it. Watch send latency closely for time-critical email like login codes, because a slow send is a failed send from the user's point of view. Queue depth and retry rate tell you your pipeline is backing up before your users do.

Business impact is where it's easy to stop short, and it's what separates a metric you watch from a metric that earns budget. Tie each email to the action it exists to drive: for a password reset, the real number is how many recipients successfully reset, not how many emails you sent. For a receipt, it's how many "where's my receipt?" support tickets you avoided. For a failed-payment notice, it's recovered revenue. These numbers connect your email system to outcomes the rest of the business cares about, and they're the ones worth putting in front of leadership.

When email isn't enough: going multi-channel

Email is the right default for most transactional messages, but it isn't always the fastest or most reliable channel, and some messages need to reach people who aren't checking their inbox. That's when you reach for SMS, push, or in-app notifications alongside email.

The channel should match the message. A login code or fraud alert often belongs on SMS or push, because it's time-critical and email latency is unpredictable. A comment notification might live best as an in-app notification with email as a backup. A receipt is fine as email, since people expect to find it there later. Going multi-channel introduces new problems: you have to respect each user's channel preferences, avoid sending the same notification four ways, and coordinate fallback logic (try push, fall back to email if it isn't delivered). This is the point where you stop thinking in terms of "sending email" and start thinking in terms of "sending notifications" across whatever channel fits, which changes how you architect the whole system.

Should you run your own mail server?

One option sits underneath all of these: running your own mail server (Postfix or a similar SMTP stack on your own IPs) instead of sending through a provider. It's worth understanding why most teams shouldn't.

The appeal is real: total control and the lowest possible per-message cost at very high volume. But you take on everything a provider otherwise handles for you:

  • IP warming. A new sending IP has no reputation. You have to ramp volume gradually over weeks before mailbox providers trust it, or your mail goes straight to spam.
  • Feedback loops and bounce parsing. You register for the feedback loops major providers offer (many only grant them to established senders), then parse bounces and complaints across dozens of inconsistent formats.
  • Reputation and blocklist monitoring. Land on a blocklist and the remediation and delisting are yours to chase.
  • Compliance and security. CAN-SPAM, CASL, and GDPR obligations, plus the operational security of an internet-facing mail server, all fall on you.

For all but the largest, most specialized senders, that's a full-time job for several engineers and rarely pays off against a provider doing it at scale. So the realistic decision for almost everyone isn't "self-host vs. provider," but which point on the provider spectrum below to choose.

Build it yourself vs. use a service

By now the scope is clear: providers, authentication, templates, localization, deliverability, suppression lists, retries, idempotency, metrics, and eventually multiple channels. The real build-versus-buy question isn't whether you can build it, but whether maintaining it is a good use of your engineering time.

It helps to think of this as a spectrum rather than a binary, because you don't have to choose between "raw ESP" and "buy everything." There are three points along it:

  1. Raw ESP. You send directly through a provider's API and build everything else yourself: templating, suppression, retries, idempotency, and your metrics pipeline. Maximum control and the lowest per-email cost. A reasonable choice when email is your only channel, your volume is modest, and your needs are stable.
  2. ESP plus your own abstractions. You stay on an ESP but build a thin internal layer around it: a send wrapper that handles retries and idempotency, a template registry, a suppression service. This is where most email systems end up, and it's a sensible middle ground. The risk is that this layer quietly grows into a notification platform you didn't plan to maintain.
  3. Notification infrastructure. You send through a service that sits above the providers and handles templating, localization, preferences, retries, and multi-channel routing through one API. Platforms like Courier fit here. You trade some control and per-message cost for not building and operating those pieces yourself.

There's no universally correct answer, and the honest version of the trade-off is about trajectory, not today. If email is all you'll ever send and you have the engineering capacity to maintain it, building on an ESP directly is fine. The moment you're juggling multiple channels, user preferences, and a growing template library, that homegrown abstraction layer from option 2 is usually the most expensive thing your team maintains, and the math starts favoring infrastructure. The useful question isn't "build or buy?" but "how much of this layer do I want to own a year from now?"

How to ship transactional email that works

Transactional email rewards getting the fundamentals right and punishes treating it as an afterthought. If you take away a handful of things from this guide, make it these.

Know what you're sending: transactional email is event-driven, one-to-one, and expected, which is what earns it inbox placement and exempts it from most marketing rules. Get the foundation right before anything else: authenticate with SPF, DKIM, and DMARC, send from a real domain, and keep transactional mail on its own stream. Choose a provider that fits your volume and stack, and lean on its API rather than raw SMTP. Build for production from the start with templates, fallbacks, retries, idempotency, and a suppression list wired to your bounce and complaint webhooks. And measure continuously, watching delivery, deliverability, engagement, and technical-health metrics so you catch problems before your users do.

Start with one email done properly, a password reset or a receipt, with authentication, a clean template, and error handling in place. Get that landing reliably in the inbox, then expand. When you outgrow a single channel or a single ESP, that's the signal to evaluate notification infrastructure that can carry the same rigor across email, SMS, push, and in-app.

Frequently asked questions

Which transactional email metric matters most?

Inbox placement rate, because it measures whether your email actually reaches users where they'll see it. Delivery rate can look high while messages quietly land in spam. After placement, track the action each email exists to drive, like password-reset completion, rather than opens.

Why are my open rates inaccurate?

Open tracking relies on a pixel, and privacy features like Apple Mail Privacy Protection pre-load that pixel whether or not the user opens the message, which inflates reported opens. Treat open rate as a directional trend and lean on click-through and task completion for accuracy.

Should I build my own transactional email system or buy one?

For almost all teams, build on an email service provider rather than running your own mail server. Whether you then add a thin abstraction layer or adopt notification infrastructure depends on trajectory: once you're juggling multiple channels, user preferences, and a growing template library, infrastructure usually costs less than maintaining that layer yourself.

Related resources:

  • Courier documentation
  • Courier API reference

Previous chapter

How to Land in the Inbox, Not Spam

Getting accepted by a mail server is easy; reaching the inbox is the real challenge. Covers SPF, DKIM, and DMARC authentication, what drives deliverability, how to avoid the spam folder, and how to handle bounces, complaints, and unsubscribes.

Multichannel Notifications Platform for SaaS

Products

Platform

Integrations

Customers

Blog

API Status

Subprocessors


© 2026 Courier. All rights reserved.