Suppression List: Automatically Stop Sending to Bounced, Complained, and Unsubscribed Addresses
Rovela automatically adds addresses to the suppression list after hard bounces, complaints, and unsubscribes, skipping future deliveries silently. This article covers the three auto-suppression triggers, the management API, and pre-flight filtering.

When a bounce, complaint, or unsubscribe happens, email providers expect you to stop sending to that address. Continuing after a hard bounce or a spam complaint damages your sending domain's reputation and can lead to your account being suspended.
Rovela's suppression list handles this automatically: certain events add addresses to the list without any action from you, and every future send to a suppressed address is silently skipped.
What gets suppressed automatically
Three events trigger automatic suppression:
Complaints (kind: complained): A recipient marks the message as spam and the ISP forwards the complaint to Rovela. Every complaint is suppressed.
Hard bounces (kind: bounced): The destination server explicitly rejects the address as non-existent. Specifically: status codes 5.1.1, 5.1.0, 5.1.3, or server text matching user unknown, no such user, mailbox unavailable, or does not exist. Only these "address doesn't exist" hard bounces trigger suppression. Transient bounces — server full, temporarily unavailable — do not.
Unsubscribes (kind: unsubscribed): A recipient uses the List-Unsubscribe one-click endpoint or the Rovela-hosted unsubscribe page.
You can also add an address manually with POST /emails/suppressions.
What happens on a suppressed send
A send request to a suppressed address is accepted normally and returns an email ID, but the message is never delivered. The status becomes suppressed. If you have a webhook configured, you receive an email.suppressed event:
{
"id": "evt_...",
"type": "email.suppressed",
"created_at": "2026-10-13T09:00:00Z",
"data": {
"email_id": "email_...",
"to": "user@example.com",
"reason": "bounced"
}
}
reason is unsubscribed, bounced, or complained.
Managing the list
List suppressions:
curl https://api.rovela.dev/emails/suppressions \
-H "Authorization: Bearer $ROVELA_API_KEY"
Returns paginated results. Each entry includes the address, kind (the suppression type), origin (how it got there: manual, bounce, complaint, or provider), and the timestamp.
Add manually:
curl -X POST https://api.rovela.dev/emails/suppressions \
-H "Authorization: Bearer $ROVELA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "kind": "unsubscribed"}'
Remove (for false positives or test addresses):
curl -X DELETE https://api.rovela.dev/emails/suppressions/$ID \
-H "Authorization: Bearer $ROVELA_API_KEY"
After removal, the next send to that address goes through. Be deliberate about removing a bounced address — if the address genuinely doesn't exist, sending again will just bounce again.
Pre-filtering before you send
For bulk sending you may want to check the suppression list before sending to avoid consuming quota on addresses that will be skipped anyway:
async function sendIfNotSuppressed(payload) {
const resp = await fetch(
`https://api.rovela.dev/emails/suppressions?email=${encodeURIComponent(payload.to)}`,
{ headers: { Authorization: `Bearer ${process.env.ROVELA_API_KEY}` } }
)
const { data } = await resp.json()
if (data.length > 0) {
console.log(`skipping ${payload.to}: ${data[0].kind}`)
return null
}
return fetch('https://api.rovela.dev/emails', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ROVELA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
}).then((r) => r.json())
}
This is optional — Rovela checks the list before delivery either way. The benefit is knowing at the call site which addresses were skipped, which is useful for logging or updating UI state.
See the Send Email API reference for the full request format.