Prompt
How do I keep email sends isolated by customer account?
Latest observation
To keep email sends isolated by customer account, treat each customer as a separate sending identity and data boundary. The main goal is: one customer’s recipients, templates, stats, suppression lists, and deliverability issues should not affect another customer’s sends.
Core approach
1) Separate data by tenant/customer
At minimum, partition all email-related records by customer_id / tenant_id:
- recipient lists
- campaigns
- templates
- unsubscribe/suppression records
- bounce/complaint events
- send logs
- sender configurations
- API keys / auth tokens
Every query should be scoped to the customer.
2) Use per-customer sender identity
If possible, each customer should send from their own:
- verified domain
- From address
- reply-to address
- DKIM/SPF/DMARC setup
This helps isolate deliverability and reputation. If one customer has bad engagement or complaints, it won’t directly harm others.
3) Separate suppression lists
Do not use one global suppression list unless that’s intentional.
Instead keep:
- customer-specific unsubscribes
- customer-specific bounces
- customer-specific complaints
Only suppress within that customer’s sending context unless you have a legal/business reason for a shared suppression layer.
4) Isolate sending infrastructure logically
If you send through a provider like SES, SendGrid, Mailgun, etc.:
- use separate subaccounts or
- separate API credentials per customer, if supported
- or at least separate mail streams / categories / tags per customer
This improves auditing and reduces accidental cross-customer effects.
5) Partition event processing
Webhook handlers for delivery/bounce/complaint/unsubscribe events should always:
- identify the tenant/customer from metadata
- verify the event belongs to that customer
- update only that customer’s records
Add a customer_id or equivalent in every outbound message metadata field so callbacks can be matched safely.
6) Enforce tenant checks at the application layer
Before sending:
- confirm the sender domain belongs to that customer
- confirm the template belongs to that customer
- confirm the recipient list belongs to that customer
- confirm quotas/rate limits are per customer
This prevents one customer from accidentally sending using another customer’s resources.
7) Consider infrastructure isolation for high-risk customers
If customers are large, regulated, or noisy, you may want stronger separation:
- dedicated IP pools per customer
- dedicated mail provider account per customer
- separate queues/workers per customer
- separate databases or schemas per tenant
This is more work, but gives stronger isolation.
Practical implementation pattern
A good model is:
customersemail_accounts(per customer sending identity)campaignsmessagessuppression_recordsdelivery_events
Each table includes customer_id, and all API endpoints are tenant-scoped.
Example outbound payload metadata:
{
"customer_id": "cust_123",
"campaign_id": "camp_456",
"message_id": "msg_789"
}
Then your webhook handler uses those IDs to update only that customer’s data.
Important gotchas
- Never share recipient lists across customers
- Never let one customer import another customer’s suppressions
- Don’t let templates or assets be globally editable unless intended
- Don’t mix stats across tenants
- Don’t rely only on UI scoping; enforce isolation in the backend/database
If you want strong isolation
Use this hierarchy:
- App-level tenant scoping
- Database row-level tenant isolation
- Provider subaccounts / separate sending domains
- Dedicated IPs / accounts for enterprise customers
If you want, I can also provide:
- a database schema for tenant-isolated email sending, or
- a reference architecture for SES/SendGrid/Mailgun multi-tenant sending.