API Documentation
Suppressions
The suppression list is the operational heart of any compliant email programme. This endpoint lets you read it, append to it programmatically, query individual entries, and remove specific addresses when an entry needs to be reversed. Four operations, well-defined data model, real consequences if it goes wrong.
Why suppressions matter
The suppression list is the system of record for "do not send" decisions across the entire programme. Every send operation, whether it goes through the REST API or SMTP relay, is filtered against this list before any message reaches the wire. An address that is suppressed cannot receive a message; an address that gets suppressed mid-campaign stops receiving the rest of the campaign.
The numerical reality is unforgiving. Gmail caps domain spam complaint rate at 0.3%, recommends staying below 0.10%, and applies the limits without warning. A 50,000-message campaign tolerates 50 complaints before the warning zone and 150 before the receiving end starts rejecting traffic outright. For a sender at 1M messages per month, that is 3,000 complaints monthly — achievable only if the suppression list is processing bounce, complaint, and unsubscribe events without dropping any of them.
Three categories of suppression dominate volume: hard bounces (invalid addresses), unsubscribes (recipient action), and complaints (FBL events from Yahoo, Microsoft JMRP, Google's complaint reporting). All three are added automatically — you do not need to write code to put them on the list. The endpoint exists for the cases where automatic addition is not enough: importing legacy suppression data during ESP migrations, honouring GDPR right-to-object requests, and managing the long tail of edge cases that automated rules do not cover.
Suppression types
The type field on every entry records why the address was suppressed. The classification matters operationally: bounce entries can be reviewed for re-engagement after a domain change, unsubscribe entries cannot be re-mailed under any circumstance, complaint entries indicate the recipient took the strongest possible negative action and must remain suppressed indefinitely.
| Type | Trigger | Auto-added | Reversibility |
|---|---|---|---|
| bounce | Hard bounce: 5xx SMTP code from receiving MTA (550 5.1.1, 550 5.1.10, etc.) | Yes | Reversible only with verification |
| soft_bounce | Persistent 4xx soft bounce after promotion threshold (3-5 consecutive failures) | Yes | Reversible if root cause fixed |
| unsubscribe | One-click List-Unsubscribe (RFC 8058) or unsubscribe link click | Yes | Only via explicit re-opt-in |
| complaint | FBL complaint from Yahoo, Microsoft JMRP, Google Postmaster Tools | Yes | Effectively non-reversible |
| spam_trap | Address matched a known spam trap pattern (pristine, recycled, typo) | Yes | Non-reversible — this is a list-quality indicator |
| manual | Added programmatically via API or imported from CSV | No | Reversible by removing the entry |
| gdpr_objection | GDPR Article 21 right-to-object exercised by recipient | Via dashboard ticket | Persistent record — do not delete |
| legal_hold | Court order, regulatory requirement, or internal compliance hold | No | Reversible only with documented authorisation |
Every entry also stores the source (which event or import created it), the occurred_at timestamp, and an optional reason note. "Suppressed" is not enough metadata for an audit; the API records all four fields so you can answer questions like "what was the most recent reason this address was suppressed, and when?" without reconstructing the history from logs.
Soft-bounce promotion
Soft bounces are temporary failures — full inbox, server overloaded, greylisting in effect. They are not a signal to stop sending immediately. The system retries soft-bounced messages on a graduated schedule, and only after consistent failure does the address get promoted from a temporary to a permanent suppression entry.
The default promotion rule:
- Retry on the same message: 3 attempts within 24 hours, with exponential backoff between attempts.
- If all retries exhaust, the message is marked deferred but the address is not yet suppressed.
- Across distinct messages, after 5 consecutive soft bounces with no successful delivery in between, the address is promoted to a
soft_bouncesuppression entry. - If a successful delivery occurs to that address before the 5-bounce threshold, the counter resets.
This rule reflects industry consensus on soft-bounce handling. Adobe Journey Optimizer, MessageGears, and SendGrid all use comparable thresholds (3 to 5 consecutive failures). Promoting too aggressively creates over-suppression of valid addresses with temporary problems; promoting too lazily damages reputation by continuing to attempt delivery to addresses that will never accept it.
List suppressions
/v1/suppressionsReturns a paginated list of suppression entries, optionally filtered by type, source, or date range. Pagination uses cursor-based next tokens rather than offset/limit because the suppression list grows monotonically and offset-based pagination drifts as new entries are added during iteration.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| type | string | Filter by suppression type (bounce, complaint, unsubscribe, manual, etc.). Comma-separated for multiple values. |
| created_after | ISO 8601 | Only return entries created after this timestamp. Useful for incremental sync. |
| created_before | ISO 8601 | Only return entries created before this timestamp. |
| limit | integer | Page size (1-1000, default 100). Use 1000 for bulk export to minimise round trips. |
| cursor | string | Cursor token from the previous response's next field. Omit for the first page. |
Example: incremental sync since the last poll
Response
Retrieve a single suppression
/v1/suppressions/{email}Looks up a specific address. Returns the full entry if the address is on the suppression list, or 404 if it is not. Useful for the "is this address suppressed?" check that some application flows need to perform before queueing a transactional send.
user+marketing@example.com is a distinct entry from user@example.com.Add suppressions
/v1/suppressionsAdds one or more addresses to the suppression list. Most senders never call this directly — bounces, complaints, and unsubscribes are auto-suppressed via the SMTP and FBL pipelines. Manual additions are for the tail: GDPR objections, legal holds, imports during ESP migration, and custom unsubscribe flows that bypass the standard mechanism.
Request body
Response
The response distinguishes addresses that were newly suppressed from those that were already on the list (already_suppressed counter). Re-suppressing an address is idempotent and does not produce an error — the existing entry is preserved with its original metadata, and a fresh source note can be appended via the optional reason field if you need an audit trail.
Remove a suppression
/v1/suppressions/{email}Removes an address from the suppression list. Use this endpoint sparingly. Most suppression entries should not be reversed — the address ended up on the list for a reason, and removing the entry without addressing the underlying cause produces immediate re-suppression on the next send.
Legitimate use cases for the DELETE endpoint:
- An address was incorrectly suppressed because of a temporary mailbox configuration issue (not a real hard bounce). After verifying the underlying problem is fixed, removing the entry restores normal sending.
- A recipient explicitly requests re-subscription after a previous unsubscribe (with documented re-opt-in evidence retained in your CRM).
- A GDPR right-to-rectification request requires the suppression record itself to be removed (rare; usually GDPR objections increase suppression coverage rather than reducing it).
complaint entries — the recipient explicitly marked the message as spam, and re-sending after that is the strongest possible signal to ISPs that you do not honour user preferences. Do not use DELETE on unsubscribe entries unless you have documented explicit opt-in evidence post-unsubscribe.The audit log retains a record of every DELETE operation, including the operator and the API key that performed it. Compliance teams can retrieve the audit trail when needed; the operation is not silent.
Bulk import patterns
The two scenarios where bulk suppression import matters: ESP migration (importing legacy suppression data on day zero of the new platform) and post-incident remediation (importing addresses identified by a list verification tool as risky).
The endpoint accepts up to 1,000 addresses per request. For larger imports, batch the operation and respect the rate limit headers in the response. A reasonable import pattern for 100,000 addresses:
For the migration use case, preserve the original suppression metadata in the reason field. Three years later, a compliance team asking "when was this address first suppressed?" needs the original timestamp, not the import date. The import date is recoverable from the API's created_at; the original suppression date is recoverable only if you put it in the reason note.
ESP migration export
The most common cause of post-migration deliverability disasters is a missing suppression list. The previous ESP had auto-suppressed bounces and complaints over years of sending; the new infrastructure starts with a clean list and immediately re-mails addresses that should never receive another message. Complaint rates spike, the warming IP gets damaged before warming finishes, and the recovery takes weeks.
The export procedure that prevents this:
- Before any send from the new infrastructure, export the full suppression list from the previous ESP. Most ESPs expose this as a CSV download or via their API; a few require a support ticket. Get this in writing as part of the migration plan.
- Normalise the data — lowercase emails, map the source ESP's suppression types to ours (most map cleanly: bounce→bounce, unsubscribe→unsubscribe, complaint→complaint), preserve original timestamps in the
reasonfield. - Import via the bulk pattern above. For 100,000 addresses, this completes in under 10 minutes.
- Verify the import. Run a sample of 50 known-suppressed addresses through
GET /v1/suppressions/{email}and confirm each returns 200 with the expected type. Do not skip this step. - Only then enable the first send from the new infrastructure.
GDPR considerations
GDPR creates two suppression-adjacent obligations that are worth treating explicitly. Neither is technically complex; both produce compliance failures when handled informally.
Article 21 right-to-object. A recipient in the EU exercising their right to object to direct marketing must be honoured immediately and indefinitely. The gdpr_objection suppression type records this. Do not delete the suppression record even if you delete the underlying contact from your CRM — the suppression record is what prevents the address from being re-mailed if it gets re-imported (which it will, if your acquisition pipeline pulls from any source other than the CRM).
Article 17 right-to-erasure. A different right with different mechanics. The recipient is asking for their personal data to be deleted, which includes the suppression record itself. The right is balanced against the legitimate interest of preventing future contact — deleting the suppression record creates the risk of re-mailing the same person, which is itself a GDPR violation. The standard practice (recommended by the Article 29 Working Party guidance and reflected in most regulatory commentary) is to retain the minimum data needed to honour the suppression: usually a one-way hash of the address, with all other fields redacted. The dashboard supports this via the GDPR → Erasure workflow; the API does not currently expose the hash-based suppression directly.
One-click List-Unsubscribe (RFC 8058). Mandatory for any sender above 5,000/day to Gmail, Yahoo, or Microsoft since the 2024-2025 enforcement waves. The unsubscribe action must be honoured within 24 hours and ideally within seconds; auto-suppression via the standard List-Unsubscribe-Post handler runs in the same flow as the response, which keeps the latency under one second in practice.
Common pitfalls
Patterns that recur in support tickets often enough to flag explicitly. Each looks innocuous in isolation; each produces measurable damage in production.
- Storing suppressions only in the application database, not synced to the API. The application database has the unsubscribe records but the send pipeline does not check it. The fix is the wrong way around: every send checks the suppression API, and the suppression API is the system of record. The application database is the cache, not the source of truth.
- Re-mailing suppressed addresses for "re-engagement campaigns." The suggestion comes up in every marketing meeting eventually. The answer is no. Sending to suppressed addresses produces an immediate complaint spike, damages reputation across all streams (not just the campaign that did it), and creates regulatory exposure under GDPR and CASL. There is no "but our segment is different" exception.
- Treating soft bounces as immediate suppressions. Soft bounces are temporary failures. The system has a graduated promotion rule (5 consecutive failures → permanent) that reflects industry consensus. Manually suppressing on first soft bounce produces over-suppression of legitimate addresses with temporary problems — corporate inboxes rejecting during DR drills, residential mailboxes full during holidays, etc.
- Not preserving suppression history during ESP migrations. See the migration section above. The most common reason for post-migration complaint spikes is mailing addresses that were already suppressed on the previous platform.
- Deleting suppression records to "clean up" the database. The suppression list grows monotonically and that is the correct behaviour. Deleting old entries removes the protection against re-mailing addresses that were suppressed for a reason. The database can hold tens of millions of entries without performance issues; "old suppressions" do not need cleaning up.
- Calling the API on every send to check suppression. The API enforces suppression at send time automatically — you do not need to pre-check before queueing a message. Pre-checking adds latency and consumes rate limit budget that you would rather spend on actual sends. The pattern works only at very low volume; at any meaningful scale, trust the system to filter.