Set Up Bounce Handling: Return Paths, Suppression and Feedback Loops
How to track your sends with a ClearMX return path, act on bounces and complaints, verify webhook signatures, and register the feedback loops that tell you about spam reports.
Verification tells you whether an address is worth sending to. Bounce handling tells you what happened once you did. Most teams have the first half and improvise the second: bounces land in a mailbox nobody reads, the same dead addresses get mailed again next month, and the sending domain's reputation pays for it. This guide sets up the second half with ClearMX — without changing how you send. You keep your ESP or SMTP relay; ClearMX gives you a return path per message, reads whatever comes back to it, keeps your suppression list, and tells your application.
One thing to be clear about first: ClearMX never carries your mail. Tracking a message does not send it, and nothing here turns ClearMX into a relay. If you only want the pre-send check, the API guide is all you need.
How bounces find their way back
Every email has two addresses that matter for bounces. The From header is the one people see and reply to. The envelope sender — also called the Return-Path or bounce address — is the one receiving servers send delivery failures to. They do not have to match, and for bounce handling they should not: the trick is to give every message its own Return-Path so a failure can be traced to the exact recipient it was for. This is called VERP (variable envelope return path), and it is what ClearMX hands you.
Return-Path: b+8fJ3kQ2mZp7Xw1LrTq9Vb0@bounce.clearmx.app
The token after b+ is random and unique to one tracked message. When a server bounces that message, the bounce arrives at that address, ClearMX matches the token, and knows which recipient, which campaign reference and which account it belongs to. A bounce whose token we do not recognise is filed away and does nothing — it can never be attributed to the wrong account.
Step 1 — Track the message
Before you hand a message to your ESP (or right after), ask ClearMX for its return path. You send the recipient, a reference that means something in your system, and the domain you send from:
POST /api/v1/tracked_messages
Authorization: Bearer <your_api_token>
Content-Type: application/json
{ "recipient": "ana@example.com", "external_ref": "campaign:12:recipient:345", "sending_domain": "news.example.com" }
The response contains verp_return_path, the address to use as the envelope sender, and suppressed — true if that recipient is already on your suppression list, in which case you should skip the send. external_ref is yours and unique per account: if your job retries and posts the same reference again, you get the same row back and nothing is charged. For campaigns, POST /api/v1/tracked_messages/batch takes up to a thousand messages in one call.
What it costs: 10 tracked messages cost one credit. Single tracks run down a prepaid counter and debit a credit when it is empty; a batch is charged up front and written completely or not at all. The suppression list, the webhooks and the sender-domain check below are free.
Step 2 — Send with that return path
Where you set the envelope sender depends on how you send:
- Your own SMTP code (Rails Action Mailer, Nodemailer, a Python
smtplibclient): set the envelope sender explicitly — in Action Mailer that ismail(return_path: …)orheaders["Return-Path"]; in Nodemailer it is theenvelope.fromoption. Do not change yourFromheader. - An ESP or relay API: look for a per-message "return path", "bounce address", "envelope sender" or "custom MAIL FROM" setting. Many providers expose one; some only allow a fixed domain. If yours cannot set it per message, you can still use ClearMX for the suppression list and sender health, and let your provider's own bounce handling feed the list through
POST /api/v1/suppressions.
Today every account uses the shared bounce.clearmx.app domain. A per-account bounce domain on your own name is planned but not available yet — do not configure DNS for one.
Step 3 — Let ClearMX act on what comes back
A returned message is classified before anything happens. Standard delivery status notifications (the multipart/report messages most servers send) are read for their status code; complaint reports from feedback loops are recognised as such; free-text bounces from older servers are matched against known phrasings; and out-of-office auto-replies are recognised in several languages so they can never suppress anyone. Then:
- A hard bounce (a 5.x.x failure — the mailbox does not exist, the domain refuses it) marks the message bounced and puts the address on your suppression list.
- A soft bounce (a 4.x.x failure — mailbox full, greylisting, a temporary refusal) is recorded; the address is suppressed only after 3 consecutive soft bounces, and a later message that went through resets the count.
- A complaint (someone pressed "report spam" at a provider with a feedback loop) suppresses the address immediately and is never downgraded by anything that follows.
- A policy block — a 5.7.x rejection saying your mail was blocked for reputation or spam reasons — is reported as a hard bounce with
policy: trueand suppresses nobody. It is a signal about your sending, not about the address; it shows up on your sender-domain card instead.
You will see all of this under Bounces and Suppressions in your dashboard, and POST /api/v1/verify now answers suppressed: true for any address on the list, at no extra credit.
Step 4 — Listen for events
Add an HTTPS endpoint under Webhooks (or with POST /api/v1/webhook_endpoints) and pick the events you want: bounce.hard, bounce.soft, complaint. Each event is one JSON POST carrying the recipient, your external_ref, the kind, the SMTP code and diagnostic, whether the address is now suppressed and why. Use Send test to receive a ping before any real bounce arrives.
Three rules for the receiving side:
- Verify the signature over the raw body. The
X-ClearMX-Signatureheader isv1=followed by the hex HMAC-SHA256 of"<timestamp>.<raw body>"under your endpoint's secret, where the timestamp is theX-ClearMX-Timestampheader. Compute it before you parse the JSON, compare in constant time, and refuse anything whose timestamp is more than five minutes old. Ruby and Node snippets are on the API page. - Answer 2xx quickly. Queue the work and return. We wait five seconds; anything else is retried at 1 minute, 5 minutes, 30 minutes, 2 hours, 12 hours and 24 hours, and after the sixth failure the endpoint is shown as failing in your dashboard.
- Be idempotent on
event_id. Retries mean you may see the same event twice. Store the id and skip repeats.
The secret is shown once when you create the endpoint. If it leaks, Rotate secret issues a new one and signs every delivery from that moment with it.
Step 5 — Sync the suppression list into your sender
Webhooks are the fast path; the list is the source of truth. Before a campaign, pull what changed since your last sync and mark those recipients unsubscribed in your own system:
GET /api/v1/suppressions?since=2026-09-01T00:00:00Z&per_page=500
You can also add addresses by hand — someone who asked to be removed on the phone, an address your previous provider already knew was dead — with POST /api/v1/suppressions or a CSV import in the dashboard, and remove one with DELETE /api/v1/suppressions/:email. The list is yours alone: it is never shared with another account, and it is never trimmed by retention. An opt-out has to outlive the message that caused it.
Step 6 — Register for feedback loops
Bounces come back on their own. Complaints do not: when someone marks your message as spam, the provider only tells senders who have registered for its feedback loop, and only about mail authenticated as theirs. Register once per sending domain:
- Microsoft (Outlook.com, Hotmail, Live) — enrol in the Junk Mail Reporting Program (JMRP) and in Smart Network Data Services (SNDS) with your sending IPs. Give
fbl@bounce.clearmx.appas the address complaint reports should go to; ClearMX reads that mailbox and treats each report as a complaint for the message it refers to. - Yahoo / AOL — apply for the Complaint Feedback Loop (CFL) for your DKIM-signing domain, again with
fbl@bounce.clearmx.appas the recipient of reports. Yahoo matches reports to senders by DKIM domain, so your DKIM must be in place first. - Gmail — has no per-message feedback loop. Verify your domain in Google Postmaster Tools instead; it shows your spam-complaint rate and domain reputation as aggregates, and if the rate climbs you will see it there before you see it anywhere else.
Step 7 — Check the domain you send from
None of this helps if receivers already distrust the domain. Under Sender domains (or GET /api/v1/sender_domains/:domain/health) ClearMX runs one DNS pass over your SPF record, your DKIM key at the selector you give it, your DMARC policy and the common blacklists, and grades each with a sentence about what to fix. Two things matter more than the rest: a DMARC policy of none means nobody is enforcing anything yet, and a DKIM key shorter than 2048 bits should be rotated. If you send from shared infrastructure, pass your sending IP as well — the IP lists are about the IP, not the name.
What you end up with
Every send carries a return path that can be traced. Dead addresses stop being mailed after one hard bounce, complainers after one complaint, and your application hears about both within a minute. Your suppression list is one call away before every campaign, and your sending domain's standing is a card you can check any time. The bounce rate on your dashboard is finally a number you can act on rather than one you find out about from a provider's warning email.
Related guides
How to compare verification tools · All alternatives
Start free with 100 verification credits. Clean lists, block disposable signups, and verify by API or CSV.