The AWS SES suppression list is not enough to protect your sender reputation
Amazon SES's suppression list only catches a narrow slice of permanently undeliverable addresses. Here's what it misses, why it matters, and how to fix it.

Tessa Reed
Over a recent 90-day window on one production Amazon SES account we work with, we counted 515,808 sends, 10,332 bounce events, and a sustained 1.6% bounce rate — with peaks past 12% on individual days, well inside the zone where AWS pauses accounts. Of the 154 distinct addresses that produced those bounces, only 38 ever reached the SES suppression list. The remaining 116 — including the three worst offenders — kept getting mail, day after day, for the full 90 days. The suppression list was working exactly as designed. That was the problem.
If you run production traffic on SES and treat the account-level suppression list as your hygiene layer, you are quietly accumulating dead addresses and sending to them on repeat. Your sender reputation pays for it. This post is about the gap between what the suppression list catches and what your bounce-rate metric actually counts — and what you need to build in that gap.
What the SES suppression list actually is
The SES account-level suppression list is a list of recipient addresses, maintained by AWS, that SES will refuse to deliver to. It is scoped per account and per region — a suppressed address in us-east-1 is not suppressed in eu-west-1.
AWS populates it automatically when you configure SuppressedReasons: [BOUNCE, COMPLAINT] via PutAccountSuppressionAttributes. Whether auto-suppression is on by default depends on your account's age and sending history. Entries auto-expire — the default retention is 14 days, configurable up to 365. You can also add entries yourself through PutSuppressedDestination or the console. When you call SendEmail or SendRawEmail to a suppressed address, SES emits a Reject event and never attempts delivery, which means you do not take a bounce for it.
So far, so useful. The catch is in one phrase from the AWS documentation: an address is automatically added when AWS receives a "hard bounce" or a complaint. Read that precisely. In SES's own classification, "hard bounce" means bounceType=Permanent with bounceSubType=General. Not every permanent bounce. Not transient bounces. Not undetermined ones. The suppression list is an automation built around one narrow signal.
Your bounce-rate metric is computed off a much broader one. That mismatch is the entire story.
References: the AWS SES suppression list documentation and the bounce and complaint notification contents.
The bounce types AWS doesn't auto-suppress
SES classifies every bounce with a bounceType and a bounceSubType. Here is the full matrix, and what AWS does with each:
| bounceType | bounceSubType | What it means | On suppression list? | Counts toward bounce rate? |
|---|---|---|---|---|
| Permanent | General | Generic permanent failure | Yes (auto) | Yes |
| Permanent | NoEmail | The address does not exist | Sometimes — depends on AWS internal classification | Yes |
| Permanent | Suppressed | SES already had it suppressed | No (already there) | Yes |
| Permanent | OnAccountSuppressionList | Same | No | Yes |
| Permanent | MailboxFull | Permanent quota exceeded (rare) | No | Yes |
| Transient | General | Accept-then-NDR (Mimecast and similar) | No | Yes |
| Transient | MailboxFull | Mailbox temporarily full | No | Yes |
| Transient | MessageTooLarge | Sender-side issue | No | Yes |
| Transient | ContentRejected | Filtered by recipient | No | Yes |
| Transient | AttachmentRejected | Filtered by recipient | No | Yes |
| Undetermined | Undetermined | SES could not classify the failure | No | Yes |
The right-hand column is the point of the table. Every one of these counts against your account-level bounce rate. AWS's reputation metric uses a broader bounce definition than its suppression-list auto-add logic. You are penalized for bounces that AWS itself refuses to put on the list.
The subtype that burns modern operators most is Transient/General, and it is worth walking through the mechanic step by step, because it is not intuitive.
A receiving Mail Transfer Agent (MTA) — most commonly Mimecast, but the same pattern shows up with Microsoft 365, Proofpoint, and Google Workspace's enterprise tier — does not always validate the recipient at SMTP time. The sequence looks like this:
- Your sender hands the message to SES, and SES hands it to the receiving MTA.
- The MTA returns
250 OK. It has accepted the message at SMTP time without checking whether the recipient exists. - SES marks the message
Delivered. - The MTA then runs its own pipeline — anti-spam, recipient validation, security analytics — on the accepted message.
- Minutes or hours later, the MTA generates a non-delivery report (NDR) and sends it back.
- SES has no live SMTP session left to retry against, so it emits a
Bounceevent withbounceType=Transient, bounceSubType=General. - AWS does not treat this as permanent enough to auto-suppress. The address stays sendable. You send to it again tomorrow, and the loop repeats.
This is responsible for the majority of bounce-rate problems at any company sending to corporate B2B addresses. Mimecast alone fronts a large share of enterprise inbound mail filtering, and every one of those domains can produce this accept-then-NDR pattern indefinitely. The address never recovers, and the suppression list never catches it.
The reputation math
A trickle of bounces that never reaches the suppression list sounds survivable. The arithmetic says otherwise.
AWS publishes account-level reputation thresholds. A bounce rate around 5% puts your account into a review status; around 10% can trigger a sending pause. The metric is a sliding window over recent sends — not since the beginning of time, but recent enough that today's behavior dominates. And critically, it counts every Bounce event regardless of whether SES considered the failure permanent enough to auto-suppress.
Now run the numbers. Suppose you have 3,000 addresses in your list that are dead but not formally suppressed — Mimecast-fronted corporate addresses, mailboxes that were deactivated, internal addresses that never existed. Send to each one once a month and you generate a baseline of roughly 100 bounces a day before a single new bounce from a healthy list. That baseline does not decay, because nothing removes those addresses. It compounds, because every campaign adds more.
It gets worse during bulk sends. Send volume and bounce volume are not linearly related across campaigns: a blast to a stale segment spikes the rate even if much of the segment is good. In the account we studied, a single campaign day hit a 12.22% bounce rate — past AWS's account-pause threshold. The account was not paused that day, but that is luck, not a margin of safety.
Plot two lines over those 90 days — the bounce rate as AWS measures it, and the cumulative count of addresses on the suppression list — and they diverge. The suppression list grows slowly and plateaus. The bounce rate keeps climbing, fed by repeat bounces to addresses the list never captured. The space between those two lines is where sender reputation goes to die.
What this looks like in a real account
The numbers in the opening are not hypothetical. They come from a SaaS company running transactional and notification mail through SES — anonymized here, because the shape is what matters, not the brand.
Over the 90-day window: 515,808 sends, 10,332 bounce events, 154 distinct bounced recipients. Of those 154, exactly 38 were on the SES suppression list. That is a 25% catch rate. Three out of four addresses that bounced stayed fully sendable.
The concentration was stark. The top three offending addresses bounced roughly 1,698 times each over the 90 days — the same three addresses, almost every day. All three were Transient/General bounces from a single Mimecast-fronted customer domain. Three addresses, each repeatedly accepted then NDR'd, none ever auto-suppressed, together producing over 5,000 bounce events.
Then there was the self-inflicted wound. For about six days, an internal misconfiguration sent mail to fake internal service-account addresses on a domain that did not accept external mail. That produced 2,028 bounces across 41 distinct addresses, all Transient/General with a DeliveryDelay: TransientCommunicationFailure signature. Because they were transient, not one of them reached the suppression list. They simply counted against the bounce rate, every day, until someone noticed.
During the worst stretch, the sustained bounce rate sat around 6.2%, with the 12.22% single-day spike inside it. After the internal-address leak was identified and filtered out, the sustained rate dropped almost immediately to about 1.6%. That is better, and still roughly three times where a healthy program should sit — a well-run list bounces under 0.5%.
The lesson is not "look at our mistakes." It is that this is what is hiding in any sufficiently old SES production account. The suppression list was doing its narrow job correctly the entire time.
What to actually do about it
The fix is a layered one. None of the layers is exotic; the failure is almost always that operators stop after the first.
Layer 1: Do not disable auto-suppression, and extend its retention. Confirm that PutAccountSuppressionAttributes has SuppressedReasons: [BOUNCE, COMPLAINT] set, and raise the suppression-list retention to the maximum your account allows — 365 days rather than the 14-day default. This is free, it takes one API call, and a surprising number of accounts have it wrong. It is the floor, not the ceiling.
Layer 2: Capture the full event stream. The suppression list is a derived artifact; the raw events are the source of truth. Wire your SES configuration sets to publish bounce, complaint, and reject events to an event destination — SNS, EventBridge, or a Firehose stream into storage. Persist every event with its bounceType, bounceSubType, and SMTP response intact. You cannot build custom suppression logic on data you never kept, and AWS will not keep it for you.
Layer 3: Build your own undeliverable list. Classify addresses as undeliverable from the full event stream, not from AWS's narrow auto-suppression rule. The rules that hold up in practice:
- Any
Bounce/Permanent/*— every permanent subtype, not justGeneral— marks the address undeliverable. - Complaints mark the address undeliverable. AWS handles this too, but verify your own code path treats complaints as permanent and never expires them.
Rejectevents mark the address undeliverable. These are sends AWS already refused; treat them as confirmed dead.- Repeated transient failures: any address with three or more transient bounces in 30 days is undeliverable. This is what finally catches the Mimecast and Microsoft 365 accept-then-NDR loop.
- Repeated undetermined failures: any address with two or more undetermined bounces in 14 days is undeliverable.
Those thresholds are illustrative — tune them to your traffic. The non-negotiable part is the enforcement point. Check the undeliverable list at the application layer, before you call SendEmail. SES will accept the API call and bill you for the attempt whether the address is dead or not, so suppressing inside SES alone is not enough; the check has to happen in your code.
Honestly assess the build-versus-buy line here. A first version of this is roughly a two-week project for one engineer if your event volume is modest. Maintaining it forever is not: the classification rules drift as MTAs change behavior, you need a materialized view that stays fast at volume, an API your applications can query at send time, and a dashboard so operations can see why a given address was suppressed. That is multiple person-quarters a year. If email is core to your business, build it and own it. If it is not, this is exactly the kind of undifferentiated infrastructure worth outsourcing — a control plane like SendOps exists to compute this second layer over your own SES account so you do not have to staff it.
The bottom line
The AWS SES suppression list is not broken. It is narrow. AWS auto-suppresses what is clearly unrecoverable and leaves everything else marked "this might still work" — which is technically defensible and operationally hostile. If you measure your bounce rate the way AWS does, where everything counts, but suppress the way AWS does, where only the narrow definition counts, the gap between those two behaviors is where your sender reputation erodes. Building the second layer is not optional for any serious SES workload. The only real choice is whether you build it before or after AWS sends you the email asking you to review your sending practices.
Running production mail on SES and not sure how wide your own gap is? You can see the suppressed-versus-actually-bounced split for your account with SendOps.