Email bounces, complaints, and suppression: a handling guide

A bounce is not just a failed message — it is a signal to the receiving provider about how well you know your own recipients. Sending repeatedly to addresses that do not exist is one of the clearest negative signals in email, and it is entirely preventable.

Updated July 31, 202614 min read
  • Hard vs soft, precisely
  • SMTP code reference
  • Suppression architecture
  • Thresholds worth alerting on

Key takeaways

  • A hard bounce means the address will never work. Sending to it again is a reputation signal, not just a wasted message.
  • The hard/soft binary is a simplification. Read the enhanced status code — 5.2.2 (mailbox full) is technically permanent but behaves transiently.
  • Complaints matter more than bounces. Keep the rate under 0.10% and never let it reach 0.30%.
  • Suppression must be enforced at send time, not as a nightly cleanup job. One retry loop against a bad address undoes the whole system.
  • Spam traps produce no bounce and no complaint. The only defence is never emailing addresses you did not collect and confirm yourself.
  • Your hygiene protects your domain reputation. The IP reputation you are also judged on is shared with every other sender on the same addresses.

Every mail system produces failures. Addresses get deleted, mailboxes fill up, domains expire, and receiving servers throttle. Handling those failures correctly is unglamorous work that turns out to be one of the highest-leverage things you can do for deliverability, because the receiving side reads your failure rate as a proxy for how carefully you manage your recipients.

The reasoning is straightforward from their perspective. A sender who knows their audience sends almost entirely to real, active mailboxes. A sender who repeatedly attempts delivery to addresses that have not existed for two years is either careless or working from a list they did not collect. Neither is a sender worth giving the benefit of the doubt.

This guide covers how to classify failures, what to do with each class, and how to build the loop so it maintains itself.

Hard bounces, soft bounces, and why the binary is imprecise

The conventional split is between permanent failures (hard) and temporary ones (soft). It maps onto the SMTP reply code classes: 5xx is permanent, 4xx is transient. That distinction is real and useful, but treating it as the whole story causes two specific mistakes — suppressing addresses that were only temporarily unavailable, and retrying addresses that will never work.

TypeSMTP classMeaningCorrect action
Hard bounce5xxThe address or domain does not exist, or delivery is permanently refusedSuppress immediately and permanently
Soft bounce4xxTemporarily undeliverable — mailbox full, server down, greylisted, rate limitedRetry with backoff; suppress only after repeated failures
Policy block5xx, often 5.7.xThe receiver refused on reputation, authentication, or content groundsDo not suppress the recipient — fix the underlying cause
ComplaintNo SMTP failureDelivered, then the recipient reported it as spamSuppress immediately; investigate the stream

The most useful precision comes from the enhanced status code defined by RFC 3463 — the X.Y.Z triplet that accompanies the reply. Its first digit repeats the class, the second identifies the subject (addressing, mailbox, mail system, network, protocol, content, or policy), and the third gives detail. Classifying on the triplet rather than the bare reply code is what lets you distinguish "no such user" from "mailbox full" from "we do not like your reputation."

A working SMTP code reference

CodeMeaningClassAction
5.1.1Bad destination mailbox — no such userHardSuppress permanently
5.1.2Bad destination system — domain does not resolveHardSuppress permanently
5.1.10Recipient address rejected — commonly a null MXHardSuppress permanently
5.2.1Mailbox disabled — exists but not accepting mailHardSuppress; may be reactivated later
5.2.2Mailbox fullPermanent class, transient realityRetry for a limited window, then suppress
4.2.2Mailbox full (transient form)SoftRetry with backoff
4.7.x / 421Rate limited, greylisted, or temporarily deferred on policySoftBack off; persistent occurrence means a reputation problem
5.7.1Delivery not authorised — policy rejectionPolicyInvestigate reputation or content; do not suppress the recipient
5.7.26Gmail: message not authenticatedPolicyFix SPF, DKIM, and DMARC alignment
5.7.515Microsoft: sending domain fails required authentication levelPolicyPublish and align SPF, DKIM, and DMARC
5.2.2 is the classic edge case: the code class says permanent, but mailboxes get emptied. Retry it for a bounded window rather than suppressing on first sight.

Complaints: the signal that matters most

A complaint is a recipient pressing "report spam" on a message that was successfully delivered. It is the most direct negative signal in email, because it is an explicit human judgment rather than an inferred one, and it is weighted accordingly.

Complaints reach you through feedback loops, where a participating provider forwards a notification when one of their users reports your mail. Coverage is uneven — some large providers participate, others expose complaint data only through their postmaster dashboards — so your observed complaint rate is a floor rather than a complete count.

Target rate

< 0.10%

Google’s recommendation

Hard ceiling

0.30%

throttling begins

Healthy transactional

< 0.01%

requested mail rarely gets reported

Denominator

Per 1,000

0.3% is 3 complaints per 1,000

Two operational points follow. First, complaints must trigger immediate suppression — a recipient who reported you once and receives another message will report again, and the second complaint costs more than the first. Second, complaint rate must be tracked per stream. A blended figure across receipts and campaigns hides exactly the problem you need to see, which is one of the arguments for separating the streams entirely.

Transactional mail generating meaningful complaint volume is itself a finding. It usually means one of three things: the messages are not genuinely transactional, the from domain is not recognisable to recipients, or people are receiving mail for accounts they did not create — which points at a signup flow without email confirmation.

Spam traps and why they are invisible

A spam trap is an address that exists solely to catch senders who should not be mailing it. It accepts mail, so there is no bounce. Nobody reads it, so there is no complaint. The only evidence you ever get is a reputation that degrades without an obvious cause.

TypeOriginHow you hit it
Pristine trapNever belonged to a person; published only where scrapers find itPurchased lists, scraped addresses
Recycled trapA real address, abandoned, then repurposed by the provider after a dormancy periodMailing addresses that have not engaged in years
Typo trapA common misspelling of a large domain, registered deliberatelySignup forms with no validation or confirmation step

The defences are entirely preventative. Never send to an address you did not collect yourself. Confirm addresses at signup rather than accepting whatever was typed. Suppress hard bounces immediately, since a recycled trap is usually preceded by a period of bouncing before the provider reactivates it. And remove long-dormant addresses from any non-transactional stream, because dormancy is exactly the condition that precedes recycling.

The handling architecture

A correct implementation has one property above all: suppression is enforced at send time, by the system that sends. A nightly reconciliation job is not sufficient, because a retry loop firing at 3am against a bad address will do the damage before the job runs.

  1. 1

    Subscribe to bounce and complaint webhooks

    At minimum email.bounced, email.complained, and email.failed. Verify the signature on every payload before acting on it.

  2. 2

    Classify on the enhanced status code

    Branch on the X.Y.Z triplet rather than the bare reply code, so mailbox-full is not treated the same way as no-such-user.

  3. 3

    Write to the suppression list

    Hard bounces and complaints suppress immediately. Soft bounces increment a counter and suppress only after repeated failures across separate sends.

  4. 4

    Enforce suppression before the message is composed

    The send path checks the list and refuses. This is the step that makes the rest work — everything else is bookkeeping if a retry can bypass it.

  5. 5

    Reflect it in your own product

    Mark the user record so your application knows the address is unreachable, and surface it in the UI so a human can correct it. A suppression the customer never learns about becomes a silent support problem.

  6. 6

    Alert on the rates, not the events

    Individual bounces are noise. A hard bounce rate crossing 2% in an hour, or a complaint rate crossing 0.1% in a day, is a signal worth waking someone for.

Handling a bounce webhook

app.post('/webhooks/supersendtx', async (req, res) => {
  if (!verifySignature(req)) return res.sendStatus(401)

  const { type, data } = req.body

  if (type === 'email.bounced' || type === 'email.complained') {
    await db.user.update({
      where: { email: data.to[0] },
      data: { emailReachable: false, emailFailureReason: data.bounce_reason },
    })
  }

  // Suppression itself is enforced provider-side: subsequent sends to a
  // suppressed recipient are refused with 422 before composition.
  res.sendStatus(200)
})
  • · Respond 2xx quickly and do the work asynchronously — webhook endpoints that block get retried.
  • · Handle events idempotently; at-least-once delivery means duplicates.

On SuperSend TX, hard bounces, permanent SMTP failures, and complaint events add to your account’s suppression list automatically, and POST /emails checks every recipient before sending — a suppressed address returns 422 with the offending recipients listed, and emits an email.suppressed event so the attempt is visible rather than silently dropped. Managed unsubscribe links write to the same list. You can list, add, and remove entries through the suppressions API.

Preventing bounces at the source

Everything above is remediation. The cheaper intervention is upstream, in the form that collects the address in the first place.

  • Validate syntax and MX at signup. A domain with no MX record cannot receive mail; rejecting it in the form costs nothing and prevents a guaranteed bounce.
  • Catch common typos. gmial.com, hotmial.com, and yaho.com are typo-trap territory. Suggest the correction inline rather than accepting it.
  • Confirm the address before sending anything else. A confirmation step is the single most effective control: it proves the mailbox exists, proves the person controls it, and prevents someone signing another person up.
  • Do not accept role addresses where they do not belong. info@, admin@, and postmaster@ are frequently shared and disproportionately generate complaints.
  • Never import a list you did not collect. Purchased and scraped lists are where pristine traps live, and one send is enough to cause lasting damage.
  • Syntax and MX validation on every address collection form
  • Typo detection for the major consumer domains
  • Confirmed opt-in before any non-essential mail
  • Hard bounces and complaints suppressed within seconds, automatically
  • Suppression enforced in the send path, not in a nightly job
  • Alerting on bounce rate above 2% hourly and complaint rate above 0.1% daily

Whose reputation are you protecting?

Every control in this guide exists to protect a reputation. It is worth asking a question most teams never do: whose? Bounce and complaint rates are scored against your sending domain, which is yours alone, and against the IP addresses your mail leaves from, which on any shared platform are not.

That asymmetry is the uncomfortable part. You can validate every address at signup, suppress hard bounces within seconds, and hold complaints under 0.02%, and still be judged partly on the behaviour of whoever else sends from those same addresses. On a general-purpose platform with a generous free tier, that population includes senders who did none of the above — and the receiving provider does not distinguish between you when it rate-limits the IP.

SignalScored againstUnder your control?
Hard bounce rateYour domain and the sending IPYour own rate, yes
Complaint rateYour domain and the sending IPYour own rate, yes — but the IP aggregate, no
Spam trap hitsPrimarily the sending IPOnly if the pool is curated
Authentication alignmentYour domainYes — entirely in your DNS
IP sending historyThe IPOnly if the IPs are yours or the pool is managed
Domain-level signals are yours to control. IP-level signals are shared with everyone else on the same addresses, which is why pool composition is a hygiene question and not just a pricing tier.

This is why SuperSend TX runs its own mail servers instead of reselling capacity. The Pool carries paying transactional customers exclusively — no free tier, no marketing campaigns, no cold outbound — so the aggregate behaviour on those IPs resembles your own. When that is not enough isolation, Dedicated gives you your own mail servers and addresses, which we provision, warm through the full ramp, and monitor per provider. Either way the discipline in this guide is enforced in the send path rather than left to your retry loop: suppression is checked before a message is composed, so a bug in your own code cannot undo it.

Clean lists deserve clean infrastructure

Suppression is enforced in the send path, so a retry loop cannot undo it — and the IPs underneath carry paying transactional customers only, or are yours alone on Dedicated, built and warmed by us.

Related reading

FAQ

Frequently asked questions

What is the difference between a hard bounce and a soft bounce?

A hard bounce is a permanent failure — the address or domain does not exist, signalled by a 5xx SMTP code — and the address should be suppressed immediately and never retried. A soft bounce is temporary, such as a full mailbox or a rate-limited server, signalled by a 4xx code, and should be retried with backoff before being suppressed after repeated failures.

What is an acceptable email bounce rate?

Keep hard bounces under 1% of sends; sustained rates above 2% indicate a list quality problem and will affect reputation. Transactional mail should be well below 1%, because the addresses come from your own signup flow rather than an imported list. A sudden rise usually points at a form that stopped validating addresses.

Should I remove an address after one bounce?

After one hard bounce, yes — a permanent failure will not become deliverable on retry, and repeated attempts to invalid mailboxes are a strong negative signal. Soft bounces should be retried with backoff and suppressed only after repeated failures across separate sends. The exception is mailbox-full, which carries a permanent code class but genuinely resolves.

What is a spam trap and how do I avoid one?

A spam trap is an address that exists only to identify senders who should not be mailing it. It accepts mail and never complains, so it produces no visible signal — only unexplained reputation damage. Avoid them by never sending to addresses you did not collect yourself, confirming addresses at signup, suppressing hard bounces immediately, and removing long-dormant recipients.

How should I handle spam complaints?

Suppress the recipient immediately — a second message to someone who already reported you costs more than the first. Then investigate the stream: complaints on transactional mail usually mean the messages are not genuinely transactional, the from domain is unrecognisable, or people are receiving mail for accounts they never created.

Does a policy rejection mean I should suppress the recipient?

No. Codes like 5.7.1, 5.7.26, and 5.7.515 are permanent, but they describe the receiver refusing you rather than the address being invalid. Suppressing those recipients quietly shrinks your reachable audience while the actual cause — authentication or reputation — stays unfixed. Classify policy rejections separately and treat a rise in them as a deliverability incident.

Can other senders on the same IP affect my bounce and complaint reputation?

Your own bounce and complaint rates are scored against your sending domain, which nobody else shares. But receivers also score the IP address, and on shared infrastructure that score reflects everyone sending from it. Perfect list hygiene does not insulate you from a neighbour who has none, which is why the composition of the pool — and whether it carries free-tier or marketing traffic — is a deliverability question.

Critical email deserves infrastructure you can name

Start on Sandbox in minutes. Move to a transactional-only Pool for production, or to Dedicated servers and IPs that we build, warm, and manage for you.