Notifications — Email, SMS & Push
How to send messages reliably at scale. Provider patterns, deliverability, unsubscribes, and async always.
The Surprising Complexity of Sending a Message
"Send the user an email when they sign up." Sounds trivial. Then production happens.
What can go wrong:
• Email bounces (typo in address, full mailbox)
• Email goes to spam
• User unsubscribed two weeks ago — sending again is illegal in most regions
• You sent the same email five times because of a retry bug
• The third-party email service is down for an hour
• Email provider rate-limits you to 100/sec; you queued 50,000
• User changed their phone number; SMS goes to someone else
• User has push notifications disabled; you sent it anyway and wasted compute
• Compliance: GDPR/CAN-SPAM/CASL — wrong consent flow = fines
Notifications look like a simple side-effect. They're actually a small system inside your system, with their own reliability, compliance, and cost concerns. Treat them with respect.
Always Send Asynchronously
The single most important rule: notifications should NEVER block your main request flow.
The wrong way:
POST /signup
├─► validate input (50ms)
├─► create user in DB (20ms)
├─► send welcome email (3000ms — third-party API call)
├─► send admin notification (2000ms)
└─► return 200 OK (total: 5070ms — user waited 5 seconds!)
The right way:
POST /signup
├─► validate input (50ms)
├─► create user in DB (20ms)
├─► enqueue welcome email job
├─► enqueue admin notification job
└─► return 200 OK (total: 80ms — user happy)
[separate worker process]
├─► picks up welcome email job
├─► calls SendGrid API
├─► retries on failure with exponential backoff
└─► moves to dead letter queue if all retries fail
Use the task queue infrastructure from Module 13. Every notification — email, SMS, push, Slack — goes through the queue. Your API stays fast. Failures don't break user-facing requests. Retries happen automatically. Spikes (a marketing blast) are absorbed by the queue instead of crashing your API.
If you take only one thing from this module: notifications belong in background jobs, never in the request path.
Email — Use a Provider, Not Your Own Server
Self-hosting an SMTP server in 2026 is masochism. Email deliverability is dominated by reputation systems (SPF, DKIM, DMARC), and a fresh IP from your cloud provider will land in spam folders for months.
Use a transactional email provider:
• SendGrid — most established, good for high volume
• Postmark — strong deliverability, simple API, slightly higher per-email cost
• Amazon SES — cheapest, requires more setup work, AWS-integrated
• Mailgun, Resend, Loops — newer alternatives with developer-friendly APIs
What they handle for you:
• Maintaining IP reputation (warm IPs trusted by Gmail, Outlook)
• SPF, DKIM, DMARC setup (DNS-based authentication)
• Bounce/complaint handling — auto-suppress addresses that bounce or mark as spam
• Tracking — opens, clicks, deliveries (with privacy considerations)
• Templates — separate content from your code; non-engineers can edit
// Typical SendGrid send
await sgMail.send({
to: user.email,
from: 'noreply@example.com',
templateId: 'd-12345', // designed in SendGrid UI
dynamicTemplateData: {
userName: user.name,
activationUrl: buildActivationUrl(user)
},
customArgs: { userId: user.id } // appears in webhooks for tracking
});
Two categories of email:
• Transactional — triggered by user action (welcome, password reset, receipt). Required for the user. High deliverability priority.
• Marketing — broadcast (newsletter, promotion). Lower priority, must respect unsubscribes, separate sending domain often.
Keep these separate. A spam complaint on a marketing email shouldn't reduce deliverability of password resets.
SMS — Costly, Powerful, Carefully
SMS reaches almost every phone but costs much more than email and has stricter regulations.
Providers:
• Twilio — the standard, global coverage, comprehensive API
• MessageBird, Vonage — alternatives with regional strengths
• AWS SNS — works for basic SMS at low cost
What's different from email:
• Cost — $0.005 to $0.10 per message depending on country (vs ~$0.0001 for email)
• Regulation — TCPA (US), GDPR consent rules. Must have explicit opt-in for marketing SMS. Auto-replies to STOP must work.
• Latency expectations — users expect SMS to arrive in seconds. Don't queue indefinitely.
• Throughput limits — carriers throttle senders that look spammy. High-volume sending requires registered short codes or 10DLC numbers.
• Two-way handling — users may reply. You need to handle inbound messages (STOP for opt-out is mandatory).
When to use SMS:
• High-priority alerts (security: "new login from new device")
• Two-factor authentication codes
• Time-sensitive transactional messages (delivery dispatched, ride arriving)
When NOT to use SMS:
• Marketing where email would do — costs 100x more
• Anything you'd send to thousands of users daily
• Anything that could wait 5 minutes — push or email is cheaper
Major SMS gotcha: international format. US numbers are 10 digits; many systems assume that. Always store and send in E.164 format (+14155551234).
Push Notifications
Push notifications are the cheapest channel — essentially free per message — but the most operationally complex.
The pieces involved:
1. User permission — the user must grant your app permission to send notifications. iOS especially: ask at the right moment, not at app launch, or they'll deny forever.
2. Device token — the OS gives your app a unique token identifying the device. Your backend stores it associated with the user.
3. Provider services:
• iOS — APNs (Apple Push Notification service)
• Android — FCM (Firebase Cloud Messaging)
• Web — Web Push API (works through browsers like Chrome and Firefox)
4. Token rotation — tokens expire, get reissued when the OS updates, change when users uninstall and reinstall. Your backend must handle "this token is no longer valid" responses and remove dead tokens.
┌──────────────┐
│ Your │
│ Backend │
└──────┬───────┘
│
POST { token, payload }
│
┌────────────┴────────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ APNs │ │ FCM │
│ (iOS) │ │(Android) │
└────┬─────┘ └────┬─────┘
│ │
┌────▼─────┐ ┌────▼─────┐
│ User's │ │ User's │
│ iPhone │ │ Android │
└──────────┘ └──────────┘
Most apps don't talk to APNs and FCM directly. They use a unified service:
• Firebase Cloud Messaging (handles both APNs and FCM)
• OneSignal, Pusher Beams, Airship — vendor solutions with dashboards, segmentation, scheduling
Things to get right:
• Don't be annoying. Push fatigue is real. Users disable notifications fast.
• Respect user preferences. Provide in-app settings for which categories of notifications to receive.
• Localize content. Sending English notifications to users with Japanese device language hurts.
• Deep link properly. Tapping the notification should open the relevant screen, not the app's home.
• Track delivery and engagement. Many notifications never open — measure what works.
Suppression, Compliance & Unsubscribes
The non-negotiable parts of any notification system:
Suppression list — A central record of "do not send to this address/number/token, ever." Unified across all your systems. Updated automatically on:
• Hard bounces (invalid address)
• Spam complaints (user marked your email as spam)
• Unsubscribe clicks
• Manual user requests
Every send checks the suppression list FIRST. No exceptions. Sending to a suppressed address risks compliance violations and damages your sender reputation.
Per-channel preferences — Users want fine control. "I want password resets but not marketing." Don't make this binary. Most apps need at least:
Account security → always on (mandatory)
Order updates → user toggle, default on
Marketing → user toggle, default off (in many regions, opt-in is required)
Recommendations → user toggle, default off
Unsubscribe links in EVERY email — required by CAN-SPAM (US), GDPR (EU), and basically everywhere else. One click should unsubscribe; don't hide it behind a login.
Logging — keep a record of what was sent to whom and when, for at least 90 days. Customer support cases ("I never got my password reset!") are unanswerable without these logs.
Audit and consent — for regulated regions, you need to prove the user consented to marketing messages. Save the timestamp, IP address, and exact wording of the consent checkbox they ticked.
The takeaway: notifications aren't just an integration. They're a regulated communication channel with users. Treat the suppression list, consent records, and audit logs as seriously as you treat your billing system.
⁂ Back to all modules