Prompt
I'm building a product that needs bounce handling and suppression lists
Latest observation
To handle bounces and suppression lists well, you’ll want to build a small email-deliverability subsystem around your sending pipeline.
Core concepts
1) Bounce handling
A bounce is when an email can’t be delivered.
-
Hard bounce: permanent failure
Examples: invalid address, domain doesn’t exist, recipient blocked permanently.- Action: usually suppress the recipient immediately.
-
Soft bounce: temporary failure
Examples: mailbox full, temporary server issue, message too large.- Action: retry for a limited time / number of attempts, then suppress if persistent.
2) Suppression list
A suppression list is a set of recipients you must not email.
Common reasons to suppress:
- Hard bounce
- Unsubscribe
- Complaint/spam report
- Manual opt-out
- Invalid address / syntax failure
- Abuse/fraud risk
- Legal/compliance reasons
Recommended architecture
A. Sending flow
Before sending each message:
- Normalize recipient email
- Check suppression list
- Check campaign-level eligibility / consent
- Send if allowed
- Record message metadata for later bounce correlation
B. Bounce ingestion flow
When your ESP/mail provider notifies you via webhook, parser, or IMAP:
- Parse the event
- Match bounce to original recipient/message
- Classify bounce type:
- hard
- soft
- complaint
- unsubscribe
- block
- Update suppression or retry state
- Log for analytics and support
Data model suggestion
suppression_list
idtenant_id/account_idemailreason
e.g.hard_bounce,complaint,unsubscribe,manual,invalidsourcee.g.ses,sendgrid,admin,importcreated_atexpires_atoptionalmetadataJSON optional
message_delivery
idmessage_idtenant_idemailprovider_message_idstatussent_atbounced_atbounce_typebounce_coderaw_eventJSON
bounce_event
idproviderprovider_event_idmessage_idemailbounce_typediagnostic_codestatus_codeoccurred_atprocessed_atraw_payload
Bounce classification rules
A useful default mapping:
-
Hard bounce
- 5xx SMTP permanent failure
user unknowninvalid recipientdomain not foundaddress rejected
-
Soft bounce
- 4xx SMTP temporary failure
mailbox fulltry again latertemporary lookup failure
-
Complaint
- feedback loop / spam complaint
- suppress immediately
-
Unsubscribe
- user opt-out
- suppress immediately for marketing, possibly still allow transactional if policy permits
Operational recommendations
Immediate suppression
Suppress immediately on:
- hard bounce
- complaint
- unsubscribe
- invalid address
- role addresses if you choose to block them (
admin@,postmaster@, etc.)
Retry policy for soft bounces
Example:
- retry after 15 min
- then 1 hour
- then 6 hours
- then 24 hours
- stop after 3–5 attempts or 72 hours
Deduplication
Use:
provider_event_idmessage_id(tenant_id, email, event_type, occurred_at window)
to avoid double-processing webhook retries.
Multi-tenant support
If your product serves multiple customers:
- isolate suppression lists by tenant
- optionally maintain a global suppression list for compliance-related entries
- be careful not to leak one customer’s suppression data to another
Important edge cases
- Plus-addressing: normalize cautiously (
user+tag@example.commay be distinct for some users, but you generally suppress at the exact address used) - Case sensitivity: treat local part case-insensitively in practice for most systems
- Alias forwarding: bounce may originate from a forwarding layer, not the original address
- Shared suppression: a single hard bounce should not suppress a different address at the same domain unless your policy says so
- Transactional vs marketing: unsubscribe should usually suppress only marketing unless the user opted out globally
Privacy/compliance
- store only necessary data
- log raw bounce payloads carefully because they may contain personal data
- support deletion requests where applicable
- keep an audit trail for suppression changes
If you’re using a provider
Most email providers already emit bounce/complaint/unsubscribe events:
- AWS SES
- SendGrid
- Mailgun
- Postmark
- SparkPost
Typically you’ll:
- configure event webhooks
- verify signatures
- map provider event types into your internal schema
- feed them into suppression logic
Minimal implementation checklist
- suppression table
- send-time suppression check
- webhook/event ingestion
- bounce classification
- retry logic for soft bounces
- unsubscribe/complaint handling
- audit logging
- idempotent processing
If you want, I can also give you:
- a database schema,
- a bounce classification algorithm, or
- a reference implementation in your preferred language.