cd /news/developer-tools/handle-bounced-email-in-agent-outrea… · home topics developer-tools article
[ARTICLE · art-29854] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

Handle Bounced Email in Agent Outreach

Nylas introduced a webhook-based bounce detection system for agent outreach campaigns. The system listens for Non-Delivery Reports (NDRs) from Google, Microsoft, iCloud, and Yahoo, converting them into structured `message.bounce_detected` events with parsed SMTP codes and reasons. This allows agents to automatically suppress bounced addresses, pause campaigns on bounce spikes, and annotate CRM records, protecting sender reputation.

read5 min views2 publishedJun 16, 2026

A failed send throws an error your code can catch; a bounce happens minutes later, in someone else's mail server, after your API call already returned success. That gap is where outreach agents quietly rot. The agent fires off a campaign, every send returns 200, the dashboard's green — and a chunk of those messages died at addresses that no longer exist. Without bounce handling you never learn which ones, so the agent keeps emailing dead mailboxes, and every retry chips away at the sender reputation your deliverable mail depends on.

The fix is event-driven: bounces arrive as webhooks, and your agent's job is to listen and adapt.

When a recipient's server rejects a message, the provider generates a Non-Delivery Report — that "Mail Delivery Subsystem" email humans glance at and archive. Nylas watches for NDRs in the sender's mailbox and converts them into a structured message.bounce_detected

webhook, with the failed address, the reason, and the SMTP code already parsed out.

Subscribe to it like any other trigger by adding message.bounce_detected

to your webhook's trigger_types

:

curl --request POST \
  --url "https://api.us.nylas.com/v3/webhooks" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{
    "trigger_types": ["message.bounce_detected", "message.send_failed"],
    "callback_url": "https://yourapp.example.com/webhooks/nylas"
  }'

For connected mailboxes, detection works on 4 providers — Google, Microsoft, iCloud, and Yahoo — because it depends on the provider issuing an NDR; generic IMAP and Exchange (EWS) accounts don't produce these events. If your outreach runs from a Nylas Agent Account (the hosted agent mailboxes, currently in beta), the platform owns the SMTP path end-to-end, so message.send_success

, message.send_failed

, and message.bounce_detected

give you send-side visibility on every outbound message.

Five fields carry the signal, per the bounce handling recipe:

{
  "type": "message.bounce_detected",
  "data": {
    "grant_id": "<NYLAS_GRANT_ID>",
    "object": {
      "bounced_addresses": "no-such-user@example.com",
      "bounce_reason": "The email account that you tried to reach does not exist.",
      "type": "mailbox_unavailable",
      "code": "550",
      "bounce_date": "Mon, 08 Jun 2026 14:21:00 +0000"
    }
  }
}

bounced_addresses

is the address that failed, bounce_reason

is the human-readable explanation, type

is a category like mailbox_unavailable

, and code

is the SMTP status — note it's a string, so compare against "550"

, not 550

. The payload also includes origin

, the original message, which is how you tie the bounce back to the campaign and contact record that triggered it.

The code

field splits bounces into two categories with opposite responses:

For an outreach agent specifically, the suppression list should sit upstream of the send, as a pre-send check in the agent's pipeline. The flow becomes: agent selects contacts → filters against suppressions → sends → bounce webhooks feed new suppressions back in. The loop closes itself, and the list only grows more accurate.

Beyond per-address suppression, a bounce stream enables campaign-level reflexes that a send-and-forget script can't have:

Back off on bounce spikes. If a batch of sends produces a cluster of hard bounces, that's a data-quality signal — a stale list, a bad enrichment source. An agent that s the campaign and flags the list for review protects the sending domain; one that plows through the remaining contacts compounds the damage. This matters doubly on agent infrastructure, where sender reputation is shared across every account on the domain.

Annotate, don't just suppress. Write the bounce_reason

and type

back to your CRM or contact store. "Suppressed: mailbox_unavailable, 550, June 2026" tells a future human (or agent) why this contact went dark.

Expect latency. Detection is asynchronous — the NDR can land minutes after the send. Don't design a flow that assumes bounce status is known immediately after the API call returns; reconcile on the webhook, not inline.

One scoping note: this covers mailbox sends. If you're sending through the transactional Email API instead, the equivalent signal is message.transactional.bounced

— one of 4 transactional deliverability events alongside complaint, delivered, and rejected.

If your outreach runs on an Agent Account, there's an enforcement layer behind your suppression list. Nylas tracks each account's rolling hard-bounce rate — soft bounces don't count — against its recent send volume, per the send limits docs:

Bounce rate Account state What happens
Under 2% Healthy Normal sending.
5% or above Under review Sending continues; sustained elevated bounces lead to a .
10% or above Sending d Outbound send requests fail until Nylas clears the .

Two details make this worth designing around rather than discovering. "Under review" is silent — your code sees nothing until the hits, at which point sends start returning 400

errors. And s don't clear on a timer: resuming requires contacting support with the cause and the fix. An agent that suppresses hard bounces aggressively stays comfortably under 2% and never meets this table; an agent that ignores them works its way down it. Complaint rates get the same treatment with much tighter numbers — review at 0.1%, d at 0.5% — so honor unsubscribes immediately too.

The minimal viable version is genuinely small: one webhook subscription, one suppressions

table with address

, code

, reason

, and date

columns, and one WHERE NOT IN

clause on your send query. You can add retry budgets and spike detection later; the suppression check alone stops the reputation bleed.

Pull your outreach agent's sends from the last month and check how many went to addresses that had already hard-bounced. If the answer isn't zero, that's your sprint.

── more in #developer-tools 4 stories · sorted by recency
── more on @nylas 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/handle-bounced-email…] indexed:0 read:5min 2026-06-16 ·