A practical operating guide to SMTP failures, enhanced status codes, retries, suppression, webhooks, and the product workflows that protect sender reputation.
A bounce is not merely a red number in an email dashboard. It is the receiving system telling you that a particular delivery attempt failed—and often giving you enough information to decide whether to retry, suppress a recipient, fix the message, slow a stream, or repair your sending configuration.
The difficult part is classification. “Hard” and “soft” bounce are convenient product labels, but real SMTP failures describe addresses, mailboxes, routing, content, authentication, policy, reputation, and temporary capacity. If every permanent response becomes “bad address,” you will suppress valid customers. If every temporary response is retried forever, you can turn a warning into a reputation incident.
First, understand where a failure happens
Your application handing a message to an email provider is not final delivery. The provider may reject the request immediately, accept it into a queue, receive a temporary or permanent SMTP response while attempting delivery, or receive a delayed delivery status notification after the destination initially accepted responsibility.
A synchronous failure arrives during the SMTP conversation: connection, sender, recipient, or message data. An asynchronous failure arrives later as a delivery status notification, often called a DSN. If you use an email service, its webhook normally converts both paths into a normalized event. Keep that normalized event for automation, but retain the original diagnostic for investigation.
- Application accepted: your sending API validated and queued the request; no mailbox outcome exists yet.
- Receiver deferred: a 4xx reply says the current attempt failed but a later attempt may succeed.
- Receiver rejected: a 5xx reply says the request failed permanently unless something changes.
- Receiver accepted: SMTP acceptance means the receiving system took responsibility, not that the message reached the inbox or was read.
- Delayed failure: a later DSN can report failure after an earlier server accepted the message.
Read the enhanced status code, not only the prose
RFC 3463 defines enhanced status codes as class.subject.detail. The first digit gives the broad result: 2 means success, 4 means persistent transient failure, and 5 means permanent failure. The subject narrows the area: X.1 is addressing, X.2 mailbox status, X.3 mail system, X.4 routing, X.5 protocol, X.6 content, and X.7 security or policy.
RESPONSE LIKELY MEANING FIRST ACTION
250 2.0.0 Accepted Record acceptance
452 4.2.2 Mailbox temporarily full Retry with bounded backoff
421 4.7.0 Temporary policy/throttling Slow down; inspect provider trend
550 5.1.1 Destination mailbox absent Suppress that recipient
552 5.3.4 Message too large Change the message; do not blame recipient
550 5.7.26 Authentication/policy failure Fix sender identity or configurationThe three-digit SMTP reply and enhanced code complement one another. Diagnostic prose is useful but receiver-specific, may change without notice, and is unsafe to use as the only machine rule. Store it for humans and provider-specific refinements; build the default decision around structured codes and delivery context.
“Hard” and “soft” are summaries, not root causes
A hard bounce normally means the sending system considers the failure permanent. A soft bounce normally means the failure is temporary and eligible for retry. Those terms are not a substitute for SMTP semantics, and providers do not always map edge cases identically.
550 5.1.1 — mailbox does not existSuppress the address in the appropriate scope and investigate its acquisition source.
452 4.2.2 — mailbox fullRetry within a defined window; decide what persistent failure means for this message type.
550 5.7.26 — unauthenticated mail rejectedStop or pause the affected stream and repair SPF, DKIM, DMARC alignment, or sender configuration.
421 4.7.0 — temporarily deferredReduce pressure and diagnose complaint, reputation, or volume patterns by receiving provider.
Classification should also reflect message purpose. A delayed newsletter can expire after several hours without harming the customer experience. A password reset that arrives two days later is both useless and risky. Retry policy belongs to the message stream and event, not only the destination server.
Build a bounded retry policy
SMTP expects temporary failures to be retried, but it does not justify rapid or unlimited attempts. Use exponential backoff with jitter, honor provider guidance where available, cap concurrent attempts by receiving domain, and set an expiry appropriate to the message.
if response.class == 2:
mark_accepted()
if response.class == 4 and now < message.expires_at:
delay = backoff(attempt) + jitter()
cap_rate(receiver_domain)
retry_after(delay)
if response.class == 5:
stop_retrying()
classify_scope(recipient | message | sender | infrastructure)
if now >= message.expires_at:
mark_expired()
notify_product_if_action_required()- Give security codes and password resets short expiries that match the token lifetime.
- Allow receipts and account notices a longer retry window, then surface failure inside the product.
- Expire time-sensitive campaigns rather than delivering an obsolete offer.
- Slow an entire receiving-domain queue when deferrals cluster there; one-message backoff is not enough.
- Stop retrying permanent failures until the thing identified by the response has changed.
Persistent 4xx responses deserve investigation even before the queue expires. A rising concentration of temporary policy failures can be an early signal of complaint, reputation, authentication, or volume trouble. Waiting for a final bounce discards valuable reaction time.
Suppress at the right scope
Suppression is a safety control that prevents a known failure from being repeated. A useful record contains reason and scope. Suppressing [email protected] because of 5.1.1 is different from pausing all mail from one tenant because its imported list is producing unknown users, or pausing a sender domain because authentication is broken.
recipient: [email protected]
reason: mailbox_not_found
scope: recipient_all_mail
source_message_id: msg_8df2
smtp_reply: 550
enhanced_status: 5.1.1
provider: receiver.example
created_at: 2026-07-27T16:42:10Z
review_policy: verified_address_change_required- Recipient scope: confirmed nonexistent mailbox, complaint, or a deliberate address block.
- Message scope: content, size, or attachment failure that can be corrected without changing the recipient.
- Stream scope: a high-risk campaign, automation, or traffic class causing concentrated failures.
- Tenant scope: one customer or integration is generating invalid destinations or abusive traffic.
- Sender scope: authentication, domain, IP, routing, or reputation trouble affects otherwise valid recipients.
Do not silently clear a permanent recipient suppression because time passed. If a customer changes the address, verify the replacement and preserve the old event. If they claim the same address is now valid, require evidence and a controlled review. Complaint suppressions should not be treated as ordinary bounces or casually reactivated.
Make bounce webhooks safe and idempotent
Delivery events can be duplicated, delayed, or arrive out of order. Your webhook handler should authenticate the source, acknowledge quickly, process asynchronously, and make repeated delivery of the same event harmless. Map the event to an immutable message and recipient record rather than trusting an address string alone.
event_id Provider event identifier for deduplication
message_id Immutable message identifier
recipient_id Internal contact or destination identifier
recipient Address as attempted
stream Transactional, marketing, tenant, or custom stream
smtp_reply Basic SMTP response, when present
enhanced_status class.subject.detail, when present
classification Temporary, permanent, policy, address, content…
receiver Destination provider or MX family
attempted_at Attempt timestamp
finalized_at Final outcome timestamp
raw_diagnostic Unmodified provider response for investigation- Verify the provider signature, token, or authenticated delivery mechanism before trusting the payload.
- Put a unique constraint on event ID or a stable provider/message/type tuple.
- Store raw and normalized forms so parsing rules can improve without losing evidence.
- Use monotonic state rules: a late “delayed” event must not overwrite a later final failure or acceptance.
- Make suppression writes idempotent and retain the first cause plus relevant subsequent evidence.
- Alert on webhook backlog, signature failures, parsing failures, and events that cannot be matched.
Measure the causes, not one blended bounce rate
“Bounce rate” is ambiguous unless the numerator, denominator, time window, and finalization rules are stated. Separate immediate permanent failures, expired temporary failures, active deferrals, application rejections, and accepted messages. Provider dashboards may use different definitions, so document yours before comparing numbers.
PERMANENT RECIPIENT FAILURE RATE
final 5.1.x recipient failures / attempted recipients
ACTIVE DEFERRAL RATE
recipients currently in 4xx retry / attempted recipients
FINAL FAILURE RATE
all terminal delivery failures / attempted recipients
Break down each by:
receiver × stream × tenant × template × acquisition source × sending domainA sudden 5.1.1 spike from one signup source suggests bad acquisition, typos, or abuse. A 4.7.x spike at one receiving provider suggests throttling or reputation. A 5.7.26 spike across providers suggests authentication. The same aggregate percentage can describe three incidents with completely different fixes.
Fix the source of invalid addresses
Suppression prevents repeat harm, but it does not repair acquisition. Trace unknown-user failures back to the exact form, import, integration, partner, tenant, campaign, and date. A small overall rate can hide a broken form or abusive customer when healthy traffic dilutes the average.
- Ask people to confirm the address they typed, especially on mobile and checkout forms.
- Use double opt-in where the risk and message program justify proof of mailbox control.
- Rate-limit signup, invitation, referral, contact, and password-reset forms to prevent mail bombing.
- Reject malformed addresses at entry, but do not pretend syntax or third-party validation proves consent.
- Never buy, scrape, append, or revive stale address lists.
- Track source quality over time and pause a tenant or integration that crosses a defined threshold.
Give customers a recovery path
When important email cannot be delivered, the product should not keep sending invisibly. Show an authenticated in-app notice, explain which address needs attention, let an authorized user replace and verify it, and notify an account administrator through an appropriate independent channel when the failure affects a team.
- Do not email the bouncing address to announce that it is bouncing.
- For security-sensitive changes, require normal authentication and additional verification.
- Show the status near the workflow that depends on email, not only in a hidden settings page.
- Offer a backup address or channel only when its ownership and privacy implications are clear.
- Stop nonessential notifications while preserving required product state and audit history.
- After correction, send a verification message before removing the relevant suppression.
Use a repeatable incident workflow
1. DETECT Confirm the metric definition and first affected time
2. SLICE Receiver, enhanced code, stream, sender, tenant, source
3. SAMPLE Read raw diagnostics and attempt history
4. CONTAIN Slow, pause, expire, or suppress at the narrowest safe scope
5. FIX Address data, authentication, content, routing, or reputation cause
6. VERIFY Send controlled traffic and watch the affected receiver/code
7. RESUME Increase gradually; preserve transactional protection
8. LEARN Add an alert, guardrail, source control, or product recovery pathStart with the narrowest coherent slice. If one tenant imported invalid recipients, contain that tenant. If one receiver is deferring a promotional stream, slow that route. If authentication is broken, pausing the sender is safer than repeatedly presenting failures to every provider.
Common bounce-handling mistakes
- Calling every 5xx response an invalid address and globally suppressing the recipient.
- Retrying a confirmed permanent failure without changing the recipient, message, or sender condition.
- Suppressing a valid address after one temporary mailbox or infrastructure failure.
- Assuming your provider’s acceptance event means the destination mailbox accepted or displayed the message.
- Deleting bounce records and losing the evidence needed to fix acquisition.
- Monitoring one account-wide percentage instead of provider, stream, tenant, and source cohorts.
- Using an email validation vendor as a substitute for consent and source controls.
- Allowing a marketing incident to consume the reputation and queues needed by critical product mail.
Bounce operations checklist
- Retain the SMTP reply, enhanced status, diagnostic, message, recipient, provider, stream, and attempt history.
- Distinguish application acceptance, SMTP deferral, permanent rejection, final acceptance, and delayed DSN failure.
- Route 4xx through bounded backoff and an expiry appropriate to the message.
- Separate recipient, message, stream, tenant, sender, and infrastructure failures.
- Suppress confirmed invalid recipients immediately without misclassifying policy or authentication errors.
- Authenticate, deduplicate, and order webhook events safely.
- Alert on provider- and code-specific shifts before a blended rate becomes dramatic.
- Trace invalid recipients to forms, imports, integrations, and tenants.
- Give customers a secure in-product way to correct and reverify an address.
- Keep critical transactional traffic isolated from riskier promotional mail.