Suppressions
The suppression list is the memory of who you must not contact. Sending to a suppressed address is refused before it leaves the queue.
List and manage
GET /email/suppressions lists suppressions; POST adds a destination by hand, with reason MANUAL or INVALID_ADDRESS; DELETE removes one you are sure about. Hard bounces, complaints and unsubscribes add themselves automatically, and entries born from a recipient's own choice cannot be deleted through the API (removable: false).
curl -X POST https://api.mailer.quathos.com/api/v1/email/suppressions \
-H "Authorization: Bearer $QUATHOS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "destination": "user@example.com", "reason": "MANUAL" }'{
"data": [
{
"id": "019f7155-2c40-7b11-a9d3-6e2b8f014c55",
"destination": "user@example.com",
"channel": "email",
"reason": "HARD_BOUNCE",
"detail": "550 5.1.1 mailbox unavailable",
"removable": true,
"created_at": "2026-07-17T18:05:36Z"
}
],
"next_cursor": null
}Reasons
| Reason | Origin |
|---|---|
| HARD_BOUNCE | The recipient server rejected the address permanently. |
| SPAM_COMPLAINT | The recipient marked a message as spam. Not removable. |
| UNSUBSCRIBE | The recipient opted out of your email. Not removable. |
| MANUAL | You added the destination yourself. |
| INVALID_ADDRESS | You marked the destination as invalid. |
| PLATFORM_BLOCK | Blocked by the platform. Not removable. |
| SOFT_BOUNCE_ESCALATED | Five soft bounces (mailbox full, temporary failure) in 14 days with no delivery in between. Temporary: expires after 30 days, and you can remove it earlier. |
Export as CSV
GET /email/suppressions/export.csv streams the whole list as a CSV attachment, with the columns email, reason, created_at, expires_at, source, detail — in that order. Two optional filters narrow it: reason= for a single reason, and since= for everything added at or after an ISO-8601 instant. There is no row limit and no pagination to walk: the file is the list.
The export needs the same read scope as listing suppressions, and it is recorded in your account audit trail as suppressions.exported, with who downloaded it and when.
Import from another provider
POST /migration/suppressions/csv uploads a CSV and applies it in the request. If the header row matches an exporter we recognise, the reason comes from the file itself instead of defaulting to MANUAL — the job report names the provider and the list it came from.
| CSV header | Read as |
|---|---|
| email,created,reason,status | SendGrid bounces → HARD_BOUNCE |
| email,created | SendGrid unsubscribes → UNSUBSCRIBE |
| address,code,error,created_at | Mailgun bounces → HARD_BOUNCE |
| address,tag,created_at | Mailgun unsubscribes → UNSUBSCRIBE |
| address,created_at | Mailgun complaints → SPAM_COMPLAINT |
| EmailAddress,Reason,LastUpdateTime | SES suppression list → per row, from Reason (BOUNCE, COMPLAINT) |
The match is on the exact header, in the order the exporter writes it. Anything else — your own spreadsheet, an export with an extra column — is read the old way: the email column is found by name and every row lands as MANUAL, which you can remove yourself.