Guide
Handle Email Bounces from the CLI
A bounced email is a message the receiving server refused, and how you react decides whether your sender reputation survives. Resend a hard bounce a few times and you look like a spammer; ignore the difference between hard and soft and you'll either drop deliverable mail or pound dead addresses. This guide explains what bounces are, how to detect them with webhooks, and how to suppress the ones that matter.
Written by Prem Keshari Senior SRE
Command references used in this guide: nylas webhook create, nylas webhook server, and nylas email send.
What is an email bounce?
An email bounce is a message the receiving mail server declined to deliver, returned to the sender as a Delivery Status Notification (DSN). The DSN format is standardized in RFC 3464, and it carries a status code from RFC 3463 that tells you why. Reading that code is the whole game — it's the difference between “try again later” and “never send here again.”
Bounces split into two classes. A hard bounce is a permanent failure (5.x.x) — the address doesn't exist, the domain is gone, the server rejected you outright. A soft bounce is temporary (4.x.x) — the mailbox is full, the server is busy, the message is too large. The class dictates the response, and conflating them is the most common bounce-handling mistake.
| Class | Status code | Typical cause | Action |
|---|---|---|---|
| Hard | 5.x.x | No such user; domain gone | Suppress; never retry |
| Soft | 4.x.x | Mailbox full; server busy | Retry with backoff |
Why do bounces matter for deliverability?
Bounces matter because mailbox providers read them as a signal of list hygiene. A sender that keeps hammering hard-bounced addresses looks like one working from a stale or purchased list, and providers respond by routing more of its mail to spam — or blocking it. Since February 2024, Google and Yahoo enforce a spam-complaint rate under 0.3% for bulk senders, per Google's sender guidelines, and repeated hard bounces feed the same reputation system.
The fix is a suppression list. The first time an address hard-bounces, record it and stop sending to it permanently; soft bounces get a bounded number of retries before they, too, are suppressed. This is non-negotiable hygiene for any system that sends at volume, and it starts with reliably detecting the bounce in the first place.
How do you detect bounces with webhooks?
The nylas webhook create command subscribes to bounce and failure events: pass the triggers message.bounce_detected (the recipient's server bounced a delivered message) and message.send_failed (the send itself failed). When either fires, your endpoint receives the event with the affected message and recipient, so you can update your suppression list immediately rather than discovering dead addresses days later. Webhook management requires an API key.
Pair those with message.send_success to confirm the happy path. Each delivery is HMAC-signed with an x-nylas-signature header, so verify it before trusting the payload — a bounce webhook is still untrusted input from a public URL. After an event, your handler classifies hard versus soft from the status code and routes accordingly.
# Subscribe to bounce and failure events (needs an API key)
nylas webhook create \
--url https://your-app.example.com/webhooks/bounces \
--triggers message.bounce_detected,message.send_failed,message.send_success \
--description "Bounce and delivery tracking"
# Receive and inspect bounce events locally over a tunnel
nylas webhook server --tunnel --secret "$NYLAS_WEBHOOK_SECRET"How do you act on a bounce?
Branch on the class. On a hard bounce, add the address to a suppression store and skip it on every future send — checking that store before each send is what keeps you off blocklists. On a soft bounce, retry with exponential backoff up to a small cap (three attempts over a few hours is typical), then suppress if it still fails, since a mailbox that's been full for a day is effectively dead.
Wire the suppression check into your send path so it's automatic. Before nylas email send runs for a recipient, confirm the address isn't suppressed; the bounce webhook keeps that store current. This closes the loop between detecting a bounce and never sending to it again. See handle email API outages for the backoff-and-queue patterns soft-bounce retries reuse, and build reliable email automation for idempotent sends.
# Suppression-aware send (pseudocode around the CLI)
# if is_suppressed(recipient): skip
# else:
nylas email send --to "$RECIPIENT" --subject "$SUBJECT" --body "$BODY"
# On a later message.bounce_detected webhook with a 5.x.x code:
# add recipient to the suppression store, permanently.Next steps
- Webhook events reference — every message and delivery trigger
- Verify webhook signatures — validate bounce payloads before acting
- Check email deliverability — read authentication results and spot failures
- Handle email API outages — backoff and retry queues for soft bounces
- Full command reference — every flag and subcommand documented