Suppressions
The addresses a workspace refuses to mail, why, and for which kind of mail: look one up, add them singly or in bulk, and lift them.
A suppression stops mail to one address in one workspace. Every send checks the list for each recipient in to, cc and bcc before anything goes out: a single send, a batch, a scheduled send at its due time, each address's turn in a campaign, and each step of an automation. A suppressed recipient is dropped from the message. When no to recipient is left, a direct send returns 202 with skipped: true and is recorded in the log as rejected, so the log can answer "why didn't this arrive?"; a scheduled send in that position is marked canceled when it falls due. See Sending.
When someone reports that they never received an email, look the address up here first with ?email=.
Suppressions are per workspace. One product's unsubscribe never silences another workspace's mail to the same person.
Scopes
Each suppression has a scope that says which mail it blocks, matched against the risk class of the message. A send's risk class is transactional unless the send says marketing; campaigns and automations are always marketing.
| Scope | Blocks |
|---|---|
all | Every message to the address, transactional and marketing. |
transactional | Transactional mail only. Marketing mail still goes out. |
marketing | Marketing mail only: campaigns, automations and sends marked marketing. Password resets, receipts and other transactional mail still arrive. |
An address can hold several suppressions, one per scope, and a message is blocked if any of them covers its risk class.
Reasons
| Reason | Set by |
|---|---|
hard_bounce | SendRaven, when SES reports a permanent bounce, or when a contact is imported with status bounced. Always scope all. Transient bounces (a full mailbox, greylisting) are never suppressed, because they resolve on their own. |
complaint | SendRaven, when a recipient marks a message as spam, or when a contact is imported with status complained. Always scope all. |
unsubscribe | SendRaven, with scope marketing so transactional mail keeps arriving: when a recipient uses the unsubscribe-from-everything link or the preference centre's opt-out, when a contact is marked unsubscribed, or when a contact is imported with status unsubscribed. Opting out of a single topic does not create a suppression. You can also set it through this API. |
manual | You, through this API or the dashboard. The default for a single add. |
list_hygiene | You, typically when importing another provider's suppression list. The default for a batch add. |
Only hard bounces and complaints are suppressed with scope all automatically. The reasons hard_bounce and complaint cannot be set through this endpoint; they can arrive through a contact import.
The suppression object
Listing and adding return the same object. Every field is always present.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (UUID). |
email | string | The address, trimmed and lower-cased. |
reason | "hard_bounce" | "complaint" | "unsubscribe" | "manual" | "list_hygiene" | Why the address is suppressed. See Reasons. |
scope | "all" | "transactional" | "marketing" | Which mail it blocks. See Scopes. |
detail | string | null | Up to 500 characters: the SMTP diagnostic for a hard bounce, the feedback type for a complaint, how someone unsubscribed, or the source of an import. null when there is none. Never set by this API. |
created_at | string (ISO 8601) | When the address was first suppressed with this scope. |
List suppressions
GET /v1/suppressions · requires emails:read
Returns suppressions newest first, cursor-paged. See Pagination.
Pass email to answer "is this address blocked, and for what?" directly: the response then holds every suppression that address has, one per scope, and an empty data array means it is not suppressed at all.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
email | string | none | Return only this address. Matched after trimming and lower-casing. |
limit | integer | 50 | Page size, at most 100. See Pagination. |
cursor | string | none | The next_cursor from the previous page. |
Example
Response
200 OK
Add a suppression
POST /v1/suppressions · requires emails:send
Suppresses one address. It takes effect on the next send: scheduled mail and running campaigns and automations re-check the list at each address's turn.
Adding is idempotent on the address and scope. If the address already has a suppression with this scope, no second row is created: the existing one keeps its id, created_at and detail, and its reason is replaced by the one you send, unless the stored reason is stronger. A hard_bounce or complaint is never replaced by manual, list_hygiene or unsubscribe, so the record of why an address is blocked survives a re-add. The response is the suppression as stored, so its reason is the one that stands. Adding with a narrower scope creates a separate suppression and leaves a broader one in place, still blocking.
With reason unsubscribe, this is an unsubscribe, and it does what an unsubscribe through the link in a message does to the person's queued mail: their pending scheduled sends are cancelled and their active automation enrolments end, whatever the scope. A scheduled send counts as theirs when they are its first to address, and it is cancelled whole, for any other recipients too; one that names them further down is not cancelled, and they are dropped from it when it falls due if the suppression's scope covers it. It does not change their contact record or topic preferences.
Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
email | string | Yes | none | A valid email address. Stored trimmed and lower-cased. |
reason | "manual" | "list_hygiene" | "unsubscribe" | No | "manual" | Why you are suppressing it. |
scope | "all" | "transactional" | "marketing" | No | "all" | Which mail to block. Use marketing for someone who asked to stop receiving newsletters, so their password resets still arrive. |
Example
Response
201 Created
A suppression object, as stored after the write. It is 201 whether or not the address was already suppressed. reason differs from the one you sent when the address already had a stronger reason with this scope, and id, created_at and detail are the existing suppression's when there was one.
Errors
| Status | Type | When |
|---|---|---|
| 422 | invalid_request | email is missing or not a valid address, or reason or scope is not one of the listed values. details lists the problems. |
Remove a suppression
DELETE /v1/suppressions/{email} · requires emails:send
Lifts one address's suppression in one scope.
A scope matches exactly. Removing with scope all deletes only an all suppression; an address that also has a marketing suppression stays blocked for marketing mail. Look an address up with ?email= to see every scope it holds.
Removal does not check the reason. Lifting a hard_bounce or complaint suppression re-enables mail to an address the receiving server said does not exist, or to someone who reported spam. Both raise the bounce and complaint rates AWS enforces on, and can get the account's sending paused. Never lift a suppression to work around a failed send.
removed is true when a suppression with that scope existed and was deleted, and false when there was nothing to remove. A false usually means the address is suppressed under a different scope, or not at all; list it with ?email= to see.
Path parameters
| Parameter | Description |
|---|---|
email | The address, URL-encoded. Not validated. Matched after trimming and lower-casing. |
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
scope | "all" | "transactional" | "marketing" | "all" | The scope of the suppression to remove. |
Example
Response
200 OK
email is the address trimmed and lower-cased, and scope the scope applied.
Errors
| Status | Type | When |
|---|---|---|
| 422 | invalid_request | scope is not one of the listed values. |
Add suppressions in bulk
POST /v1/suppressions/batch · requires emails:send
Suppresses up to 10,000 addresses with one reason and one scope. This is the migration path: import your previous provider's unsubscribe list before your first campaign, or everyone who already opted out there is mailed again here.
The defaults differ from a single add. A batch defaults to reason list_hygiene and scope marketing, because an imported opt-out list is about marketing mail. Pass scope: "all" explicitly for a list of hard bounces or complaints.
Addresses are trimmed and lower-cased, then de-duplicated, and written in one bulk operation. Each is written the same way as a single add: re-running an import creates nothing new, and an address already suppressed with the same scope has its reason replaced unless the stored reason is a hard_bounce or complaint. The request returns after every address has been written.
With reason unsubscribe, every address's pending scheduled sends are cancelled and its active automation enrolments end, as for a single add.
Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
emails | array of string | Yes | none | 1 to 10,000 valid email addresses. One invalid address fails the whole request with 422 and nothing is written. |
reason | "manual" | "list_hygiene" | "unsubscribe" | No | "list_hygiene" | Applied to every address. |
scope | "all" | "transactional" | "marketing" | No | "marketing" | Applied to every address. |
Example
Response
201 Created
| Field | Type | Description |
|---|---|---|
suppressed | integer | The number of distinct addresses written, including any that were already suppressed with this scope. |
duplicates | integer | How many entries in emails repeated an earlier one after trimming and lower-casing. |
Errors
| Status | Type | When |
|---|---|---|
| 422 | invalid_request | emails is missing, empty, longer than 10,000 or contains an invalid address, or reason or scope is not one of the listed values. details lists the problems. |
Remove suppressions in bulk
DELETE /v1/suppressions/batch · requires emails:send
Lifts the suppression with the given scope from up to 10,000 addresses. The body goes in the request, even though this is a DELETE.
The scope matches exactly, as in Remove a suppression, and the same warning applies: removal does not check whether a suppression was a hard bounce or a complaint.
Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
emails | array of string | Yes | none | 1 to 10,000 valid email addresses. Matched after trimming and lower-casing. |
scope | "all" | "transactional" | "marketing" | No | "all" | The scope of the suppression to remove from each address. |
Example
Response
200 OK
removed is the number of suppressions that existed with that scope and were deleted. An address with no suppression in that scope, or repeated in the request, is not counted.
Errors
| Status | Type | When |
|---|---|---|
| 422 | invalid_request | emails is missing, empty, longer than 10,000 or contains an invalid address, or scope is not one of the listed values. details lists the problems. |