Every email-marketing operator has a sense that bounces are bad. The deeper truth is that bounces are diagnostic — different bounce types carry different signals. A spike in hard bounces means one thing (list hygiene); a spike in soft bounces means a very different thing (sender reputation event). Reacting wrongly to a bounce surge — pruning the list when the problem is reputation, or warming the IP when the problem is data quality — burns budget and time.
This article walks the bounce taxonomy AcelleMail uses (hard, soft, unknown — see app/Model/BounceLog.php), what each means at the SMTP level, what AcelleMail does automatically with each, and the patterns that should prompt operator action.
The three AcelleMail bounce categories#
AcelleMail's BounceLog model defines three constants:
public const HARD = 'hard';
public const SOFT = 'soft';
public const UNKNOWN = 'unknown';
A BounceLog row is created via recordHardBounce() or recordSoftBounce() (or record() with an explicit type) when the bounce-handler IMAP poller encounters a non-deliverable status notification, or when the synchronous SMTP exchange returns a permanent failure. The row carries:
tracking_log_id — points at the original send
customer_id — for multi-tenant isolation
bounce_type — one of the three constants above
runtime_message_id — the Message-ID the receiver saw
raw — the full DSN body (preserved verbatim — read it when in doubt)
Hard bounces — permanent failures#
Hard bounces are the SMTP equivalent of "this address will never accept mail." AcelleMail marks them by:
- 5xx responses during the SMTP transaction — the receiver synchronously rejects with
550, 551, 552, 553, 554 etc.
- Async DSNs with
5.x.x in the status field — RFC 3463 enhanced codes returned by the receiving MTA via bounce email.
The semantics across the 5xx range:
| SMTP code |
DSN code |
Meaning |
Action |
| 550 |
5.1.1 |
User unknown |
Suppress permanently |
| 550 |
5.1.10 |
Recipient address rejected (user not found in directory) |
Suppress permanently |
| 550 |
5.7.1 |
Sender denied (policy block) |
Reputation issue — investigate |
| 550 |
5.4.6 |
Routing loop / too many hops |
Misconfigured MX — check recipient's setup |
| 552 |
5.2.2 |
Mailbox over quota |
Soft in spirit, hard if persistent — see edge cases |
| 553 |
5.7.1 |
Mailbox not allowed |
Domain block — investigate |
| 554 |
5.7.1 |
Refused |
Reputation block at edge filter |
The crucial distinction: 5.1.1 (user unknown) is a list-hygiene issue. 5.7.1 (policy denied) is a reputation issue. AcelleMail records both as bounce_type = hard, but the corrective action is different. Always read the raw field of the bounce log when investigating sustained hard-bounce rates above 2 %.
AcelleMail's auto-suppression: hard bounces are added to the customer's bounce_list (a per-tenant suppression table). Future campaigns from that customer skip suppressed addresses automatically. You can't undo a suppression by re-importing the address — you must explicitly remove it from the suppression list.
Soft bounces — transient failures#
Soft bounces are deferrals: "I can't accept this right now, try later." Common SMTP causes:
- 4xx responses during the SMTP transaction (
421 service not available, 450 mailbox unavailable, 451 local error).
- Async DSNs with
4.x.x status fields — the receiving MTA queued and then deferred.
AcelleMail's worker queues a retry on a 4xx (Laravel queue retry semantics; configured in the queue connection). Retry interval grows with attempt count: 5 min → 15 min → 1 hour → 6 hours.
Common soft-bounce diagnostics:
| Code |
Meaning |
Action |
| 421 |
Service not available; throttling |
Reduce send rate — likely reputation event |
| 421 RP-002 |
Microsoft "rate-protected" defer |
YELLOW SNDS — pause Outlook sending |
| 421 4.7.0 [TSS04] |
Yahoo reputation defer |
Pause Yahoo sending; reduce volume |
| 450 4.2.1 |
Mailbox temporarily disabled |
Re-attempt — auto-resolves |
| 451 4.4.2 |
Local error in processing |
Receiver-side problem — usually transient |
| 452 4.2.2 |
Mailbox over quota |
Sometimes promotes to hard if persistent |
The soft-bounce trap is that a sender reputation event manifests as soft bounces from many recipients simultaneously. If you have a sudden surge of 421 codes, it's almost never a transient receiver-side problem — it's your IP getting throttled. The symptom looks like "deferrals," but the underlying cause is reputation. Pruning the list won't help; pausing volume + investigating reputation will.
Unknown bounces — the FBL bucket#
bounce_type = unknown is what AcelleMail uses for Feedback Loop (FBL) abuse reports — when a recipient clicks "Report Spam," their ISP sends a feedback message in Abuse Reporting Format (ARF), and AcelleMail's FeedbackLoopHandler ingests it. The constants:
public const FEEDBACK_TYPES = ['abuse', 'fraud', 'virus', 'other', 'not-spam'];
public const EXCLUDED_FEEDBACK_TYPES = ['not-spam'];
The not-spam type is excluded — that's a "not spam" override from the recipient and isn't a complaint at all. Everything else gets logged as a complaint and adds to the customer's complaint rate.
FBL handling differs from hard/soft bounces:
- Auto-action: the recipient is added to the customer's
complaint_list (separate from bounce_list).
- Threshold: Gmail tolerates ≤ 0.10 % complaint rate; Outlook ≤ 0.40 %. Above either, ISP-level reputation drops.
- Reading the source: the
raw field contains the original ARF report — Feedback-Type: abuse, Original-Mail-From:, etc. Useful for verifying the FBL is correctly configured.
What patterns mean what#
Once you have a few weeks of bounce log data, the patterns tell you the story. Three distinct shapes:
Pattern A — list hygiene problem#
- Symptom: sustained hard bounce rate 3-8 %, mostly
5.1.1 user unknown.
- Cause: list contains old addresses, typos, role addresses, single-opt-in junk.
- Action: re-engagement campaign + prune zero-engagement subscribers. Don't pause sending.
Pattern B — reputation event#
- Symptom: surge of soft bounces (
421, 4.7.0, 4.7.1) across many recipients in a short window.
- Cause: ISP throttling or grey-listing your IP.
- Action: pause new volume immediately; send only to engaged segment for 7 days; investigate Postmaster Tools / SNDS.
Pattern C — content / FBL spike#
- Symptom: complaint rate jumps to 0.5-2 % on a specific campaign.
- Cause: subject line, content, or unsubscribe-link change is triggering reports.
- Action: review the campaign, audit the unsub link, audit the engagement segment.
The pattern matters more than the absolute rate. A 2 % bounce rate with the Pattern A shape is fixable; a 0.5 % rate with the Pattern C shape is more dangerous.
AcelleMail-specific operations#
Two daily checks:
# 1. Bounce-rate-by-cause for the last 24h:
cd /var/www/acellemail
php artisan tinker --execute='
$rows = \App\Model\BounceLog::where("created_at", ">=", now()->subHours(24))
->selectRaw("bounce_type, count(*) as n")
->groupBy("bounce_type")
->get();
foreach ($rows as $r) echo "$r->bounce_type: $r->n\n";
'
# 2. Top-10 5xx codes over the last 24h:
mysql -e "
SELECT SUBSTRING_INDEX(raw, ' ', 2) as code, COUNT(*) c
FROM bounce_logs
WHERE created_at > NOW() - INTERVAL 24 HOUR AND bounce_type = 'hard'
GROUP BY code ORDER BY c DESC LIMIT 10;
" acellemail
Anything outside of 5.1.1 user unknown and a small 5.0.0 / 5.7.1 baseline warrants a closer look.
Related reading#
FAQ#
Should I retry hard bounces?#
No. AcelleMail suppresses hard-bounced addresses; re-sending forces another bounce, which doubles the reputation damage with no upside.
When does a soft bounce get promoted to hard?#
After the configured retry budget is exhausted (default 4 retries over ~7 hours). The original soft bounce row stays as soft; a new row is logged as hard if the final retry returns 4xx-then-5xx or all retries 4xx.
What about transient 4.7.0 codes from Gmail specifically?#
Gmail's 4.7.0 is "may have something to do with your reputation." It's a soft bounce by category but a reputation signal by content. Treat it like Pattern B (reputation event) regardless of frequency.
Can I bulk-delete bounce records to "reset" a list?#
You can delete BounceLog rows but the suppression-list entries (bounce_list, complaint_list) are separate tables that survive bounce-log cleanup. Don't bulk-delete suppressions — re-introducing previously-bounced addresses is the fastest way to tank reputation.