Prompt
How do I build multi-tenant email sending for each customer domain?
Latest observation
To build multi-tenant email sending per customer domain, think in terms of:
- Tenant isolation
- Domain authentication
- Routing and sending
- Tracking and compliance
Here’s a practical blueprint.
1) Decide what “multi-tenant” means for email
Usually there are two patterns:
A. One shared sending infrastructure
- One app/platform sends on behalf of many customers.
- Each customer has their own “From” domain, branding, and tracking domain.
- You manage all auth records centrally.
B. Per-tenant dedicated sending
- Each customer gets separate infrastructure:
- dedicated subdomain
- dedicated IP pool or provider account
- separate suppression lists and analytics
- Better isolation, more operational overhead.
For most SaaS products, shared infrastructure with per-tenant domains is the best starting point.
2) Require tenants to verify their domain
You should never let a customer send from a domain they haven’t proven they own.
Verification flow
- Tenant enters:
- sending domain:
mail.customer.comorcustomer.com - optional tracking domain:
click.customer.com
- sending domain:
- Your system generates DNS records.
- Customer adds them to DNS.
- You verify automatically.
Common records
For sending:
- SPF
- DKIM
- DMARC (recommended)
For tracking:
- CNAME for open/click tracking domain
Example:
customer.comsends asnoreply@customer.com- DNS includes:
- SPF TXT
- DKIM TXT or CNAME
- DMARC TXT
- tracking CNAME like
links.customer.com -> your-tracking-domain.example.com
3) Use a provider that supports multiple identities
Good options:
- SES
- SendGrid
- Mailgun
- Postmark
- SparkPost (depending on use case)
You need support for:
- verified sending domains
- API-based sending
- per-message headers or metadata
- webhooks for bounces, complaints, deliveries
Important
If you need each tenant to send from their own domain, make sure your provider can:
- verify many domains
- handle bounce webhooks
- separate reputation/tracking per domain or tenant
- support configuration sets / tags / streams if needed
4) Design your data model
You’ll want tables/entities like:
Tenants
idnamestatus
EmailDomains
idtenant_iddomainverification_statusspF_statusdkim_statusdmarc_statustracking_domainprovider_identity_id
SendingIdentities
idtenant_idemail_domain_idfrom_namereply_toprovider_accountactive
EmailMessages
idtenant_idrecipientsubjectbodyfrom_addressprovider_message_idstatusmetadata
Suppressions / complaints / bounces
- store per tenant and possibly globally if you need shared blocklists.
5) Route every email through the tenant context
When your app sends an email, it must know:
- which tenant it belongs to
- which verified domain to use
- which template/branding to apply
Example flow:
- User action triggers email send.
- App loads tenant settings.
- Picks verified sending domain.
- Builds message:
- From:
Brand <noreply@customer.com> - Reply-To:
support@customer.com - Headers include tenant ID or metadata.
- From:
- Sends through provider.
Never allow arbitrary From addresses without validation.
6) Authenticate each domain correctly
SPF
Authorize your email provider to send for the domain.
DKIM
Sign outgoing email with a domain-specific key.
DMARC
Tells receiving mail servers how to handle failures.
Best practice:
- Use at least DKIM + SPF
- Align the visible
Fromdomain with authenticated domains - Add DMARC once setup is stable
If you send from customer.com, ideally:
- SPF passes for the envelope sender
- DKIM signs with
d=customer.com - DMARC aligns with
customer.com
7) Consider subdomains instead of apex domains
Often easier:
mail.customer.comnotify.customer.commg.customer.com
Why:
- safer for customers
- easier DNS delegation
- less risk of interfering with their primary business email
- cleaner SPF/DKIM isolation
Many SaaS apps use:
email.customer.comfor sendinglinks.customer.comfor tracking
8) Handle bounce, complaint, and unsubscribe events per tenant
This is critical.
Use provider webhooks to receive:
- bounce
- complaint
- delivery
- open/click events
- unsubscribe
Then:
- map event back to tenant and message
- update suppression state
- prevent future sends to bad addresses
- expose analytics in tenant dashboard
Keep suppression lists isolated:
- tenant A’s complaint should not suppress sends for tenant B
- unless you intentionally use a global suppression policy
9) Build a queue-based sending pipeline
Don’t send synchronously from your web request.
Use:
- API request creates email job
- queue/worker picks it up
- worker resolves tenant domain + templates
- worker sends via provider
- worker updates status
This gives:
- retries
- rate limiting per tenant
- better observability
- safer scaling
10) Rate limit and protect reputation
You’ll need controls like:
- max sends per tenant per minute/hour/day
- warm-up for new domains
- blocked sends if domain not fully verified
- reputation monitoring
- bounce thresholds
If one tenant sends spam, it can hurt shared infrastructure. Add:
- content checks
- abuse detection
- manual review for high-risk tenants
11) Support templating and branding per tenant
Store tenant-specific:
- logo
- colors
- footer text
- legal address
- unsubscribe link
- support email
Use templates like:
- welcome email
- verification email
- invoice email
Render with tenant context so every message looks like it came from that customer’s brand.
12) Security and compliance
Important safeguards:
- only allow verified domains
- audit all domain changes
- log who approved DNS verification
- encrypt secrets and API keys
- use per-tenant access controls
- support unsubscribe and GDPR/CCPA deletion workflows
- avoid exposing raw provider credentials to tenants unless necessary
13) A simple architecture
Example components
- Tenant service: stores customer settings
- Domain verification service: creates and checks DNS records
- Email sending service: prepares and sends messages
- Webhook service: processes bounces/complaints
- Analytics service: per-tenant reporting
Flow
- Tenant adds domain
- You generate DNS instructions
- Customer verifies DNS
- Domain marked active
- App sends from that domain
- Webhooks update status and metrics
14) Example sending logic
Pseudo-flow:
if tenant domain is verified:
from_address = tenant's verified from address
dkim_domain = tenant's domain
tracking_domain = tenant's tracking domain
send via provider with tenant metadata
else:
reject send
15) Start simple, then add sophistication
MVP
- one provider
- tenant table
- domain verification
- send emails via queue
- webhook processing
- basic dashboard
Later
- dedicated IPs
- regional sending
- advanced suppression rules
- tenant-level analytics
- provider failover
- custom DKIM key rotation
- per-tenant sending pools
Recommended best practice setup
If you want a strong default:
- Use subdomains for sending
- Require SPF + DKIM + DMARC
- Send through a queue
- Keep tenant metadata on every email
- Process webhooks for bounces/complaints
- Maintain per-tenant suppression lists
- Add rate limiting and abuse detection
If you want, I can also give you:
- a database schema
- a reference architecture
- provider-specific setup for SES / SendGrid / Mailgun
- a Node.js / Python implementation example