Building a bounce-class taxonomy that pauses the right campaign
Hard versus soft is a useful starter, and a terrible ops system. Treat every 5xx as "kill the address" and you suppress valid prospects after a policy block. Treat every bounce as "pause the whole client" and one bad list segment freezes warm domains that were fine.
Agencies need a bounce-class taxonomy that maps SMTP reality to campaign actions. GTM keeps outbound in client-isolated workspaces with send windows, domain limits, and suppression that survives purge. The taxonomy is how you decide whether the next action is suppress an address, retry, pause one campaign, or stop the domain.
Start from codes, not marketing labels
Classify from the SMTP reply and enhanced status code (RFC 3463 style), not from the human-readable text. Providers word failures differently. Codes stay more stable.
Practical buckets that map to actions:
- Address failure (often 5.1.x): mailbox does not exist, domain invalid, user unknown. Suppress the address in this client workspace. Do not retry.
- Mailbox temporary (often 4.2.x, mailbox full): retry on a schedule. Convert to suppress only after repeated failures across enough attempts or days.
- Policy / block (often 5.7.x): authentication, reputation, or policy refusal of a likely-valid address. Investigate sender-side. Do not auto-suppress the prospect as "bad email."
- Content / filter (content or phishing-style rejects): pause or rewrite the offending step. Check that campaign's copy and links before blaming the list.
- Technical / routing (4.4.x / 5.4.x family): DNS, relay, or transient infrastructure. Retry carefully. If the same code hits many unrelated recipient domains, look at your side first.
- Complaint / feedback loop (not a bounce, but same ops board): treat as campaign and list quality fire. Pause fast if rates climb.
Hard and soft still matter as a first digit rule: 4xx tends to mean retry, 5xx tends to mean permanent. The enhanced code is what stops you from confusing a dead mailbox with a reputation block.
Map each class to a pause scope
This is the agency-specific part. One bounce class should not always mean "stop the workspace."
| Class | Default address action | Default campaign action | Escalate to domain pause when |
|---|---|---|---|
| Address failure | Suppress contact | Continue if rate is normal | Hard bounce rate spikes on a list segment |
| Mailbox temporary | Retry, then suppress if persistent | Continue | Same mailbox class dominates a domain |
| Policy / block | Keep contact, flag for review | Pause that campaign or stream | Blocks spread across campaigns on one domain |
| Content / filter | Keep contact | Pause or rewrite the step | Multiple steps trip the same filter |
| Technical / routing | Retry | Continue unless widespread | Errors cluster on your sending infra |
| Complaint | Suppress or honor preference | Pause campaign immediately | Domain complaint rate approaches provider ceilings |
Google's Postmaster guidance pushes senders to keep spam rates low (under 0.3%, ideally under 0.1% in Postmaster Tools). Complaint spikes are domain-level problems even when they start in one sequence.
Build the taxonomy as a living table
Put it in the client ops doc, not in a Slack thread:
- Class name
- Example SMTP / enhanced codes
- Address action
- Campaign action
- Domain action
- Owner who can override
- Last reviewed date
Require a human override for policy/block and for any domain-wide pause. Auto-suppress on address failure. Auto-retry on soft mailbox classes. Never let a single vague "bounce" webhook decide all three scopes the same way.
Thresholds without fake precision
Use thresholds as tripwires, not as science theater. Common healthy ranges cited in deliverability ops put total bounce rate ideally under ~1–2%, with hard bounces kept very low. Exact cutoffs should match the client's volume and history. What matters more than copying someone else's percentage is defining:
- Rolling window (for example last 24 hours and last 7 days)
- Denominator (attempted sends for that campaign or domain)
- Who gets paged
- Whether pause is campaign-only or domain-wide
If hard-address failures concentrate in one imported segment, pause that segment and keep other campaigns alive. If policy blocks appear across segments on one sending domain, pause the domain and check auth, reputation, and warm-up state.
How this plays with multi-client isolation
In a shared agency platform without isolation, one client's bad list can teach operators to pause everything. Client-isolated workspaces make the better pattern possible: suppress and pause inside the client that earned the bounce. Suppression should survive purge so a cleanup job does not re-enable dead addresses. Sequence stop-on-reply still applies for human replies; bounce classes are a parallel control plane for delivery failures.
Rollout steps for an agency ops team
- Inventory the bounce payloads your ESP or outbound tool already emits.
- Map each payload into the six classes above.
- Wire suppress / retry / campaign-pause / domain-pause actions to those classes.
- Add a daily bounce board per client: class counts, top campaigns, open policy blocks.
- Review the map monthly. Provider wording drifts. Your actions should not.
A taxonomy that only says "hard" and "soft" will keep pausing the wrong thing. A taxonomy that names the failure and the scope keeps one bad segment from taking down a whole workspace.
Sources: SMTPedia bounce categorization, MCSNET bounce classification, Qualisend bounce classification 2026, Google Email sender guidelines, GTM product knowledge.
