All posts
2 min readPigeonify

Hard bounces, soft bounces, and what to do with each

Not every bounce means the same thing. Treating them identically either burns your reputation or loses you customers.

DeliverabilityBounces

A bounce is a receiving server telling you it will not deliver your message. The reason determines whether you should ever try that address again.

Hard bounces: stop immediately

A hard bounce is permanent. The mailbox does not exist, the domain does not resolve, or the server has explicitly refused you.

550 5.1.1 User unknown

Retrying achieves nothing except proving to the receiver that you do not process bounces — which is itself a spam signal. Suppress the address on the first hard bounce. Not the third. The first.

Keep hard bounces under 2%. Amazon SES places accounts under review above 5%, and that review can end in a sending pause.

Soft bounces: retry, then give up

A soft bounce is temporary. The mailbox is full, the server is overloaded, or you are being rate limited.

452 4.2.2 Mailbox full
421 4.7.0 Too many messages from your IP

These are worth retrying with exponential backoff. But a mailbox that has been full for two weeks is abandoned. After four or five failures across several days, treat it as permanent and suppress it.

Blocks: a different problem entirely

Sometimes the rejection is about you, not the recipient:

554 5.7.1 Message rejected due to poor reputation

Retrying makes this worse. This is a signal to stop and investigate — check your complaint rate, whether your IP is on a blocklist, and what you sent recently.

Complaints outrank everything

A complaint is someone pressing "this is spam". It is not a bounce; the message was delivered and then rejected by a human.

Suppress immediately and permanently. Never require a second complaint. And treat a rising complaint rate as an emergency rather than a metric — it is the fastest route to being blocked wholesale.

Building it properly

Bounce data arrives asynchronously, minutes or hours after the send. You need a webhook, not a response code.

Three things your handler must get right:

Verify the notification is genuine. An unauthenticated bounce webhook lets anyone suppress any address on your platform.

Store the message ID. It is how a bounce that arrives an hour later is matched to the send that caused it. Index that column — at scale it is queried on every event.

Make suppression the send path's concern. A suppression list only works if something checks it before every send. A list nothing reads is just a table.

Send email that actually arrives

Authentication, suppression and bounce handling built in. Start free, no card required.

Start free