SMTP status codes are the receiving server talking. Your ESP may label them soft and hard, but you should still read the enhanced status and the text. Misclassification keeps bad addresses on the list or drops good ones. Treating every bounce as a retry is how you annoy both the recipient domain and your own queue.
Hard bounces
Permanent failures belong off the list the same day. The mailbox does not exist, the domain does not exist, or the server will not relay for a policy that will not change. Sending again looks like a directory attack. Typical 5xx codes with “user unknown” or “mailbox unavailable” belong here, unless the text says the mailbox is full, which is often temporary.
Do not wait for a human weekly review to suppress a 550 user unknown. Hook the DSN or the ESP event into suppression before the next campaign. If a whole domain starts returning NXDOMAIN, that is a DNS or shutdown event, not a reason to keep the leftover local-parts around “in case they come back”.
Soft bounces
Temporary problems include greylisting, rate limits, a full mailbox, and an outage. Retry with backoff. If the same address soft-bounces across many days, treat it as abandoned and suppress. A 4xx from a provider during warmup is often “slow down”, not “delete this user”.
Greylisting can look scary in a dashboard because the first attempt fails and the second succeeds. If you classify that as a hard fail, you will delete working addresses. If you ignore a 4xx that repeats for two weeks, you will keep trying a mailbox that is gone.
Blocks that look like bounces
Some 550s are policy: spam content, failed authentication, or an IP on a blocklist. Those are not bad recipients. They are a reputation problem. Fix SPF, DKIM, and DMARC, cut the campaign, and check listings before you purge the list. Wiping engaged users because Microsoft rejected a burst is how you shrink the only audience that was helping you.
Read the text. “User unknown” and “blocked using” are different sentences. Your mapping table should keep a few provider-specific patterns, because Gmail, Microsoft, and Yahoo do not speak identical English.
What to do in the application
Store the raw DSN, not only the ESP’s soft/hard flag. Count unknown-user rate per campaign. Alert when it crosses a low threshold, such as 1% on a mature list. During warmup the threshold is even lower. Pair bounce class with list source so a bad import cannot hide inside a healthy newsletter.
Give on-call a runbook: hard unknown, suppress; mailbox full, retry then sunset; policy block, stop sending that stream and check auth and listings; deferral storm, reduce concurrency to that provider. The runbook is the difference between a bounce handling system and a folder of screenshots.
Codes worth memorizing
- 421 / 450: try later, often rate or greylist. Slow down.
- 452 mailbox full: retry, then suppress if it persists.
- 550 5.1.1 user unknown: suppress now.
- 550 policy / spam / blocklist: not a recipient problem.
- 554: often a hard policy reject. Read the text before you decide.
If your vendor UI hides the enhanced status, export the raw events. You cannot operate mail if the only word you have is “bounced”.


