MillionSend Docs

Suppressions

Automatic protection for your sender reputation.

The suppression list protects your sender reputation — and your SES account — by making sure you never repeatedly mail an address that hard-bounced or marked you as spam.

How addresses get suppressed

  • Hard bounce — the receiving server permanently rejected the address.
  • Complaint — the recipient marked a message as spam.
  • Unsubscribe — the recipient opted out of all marketing email, through the one-click header or the hosted preference page. Besides flagging the contact, the opt-out is retained here so it survives deleting and re-importing the contact; only an explicit PATCH /contacts/{id} with unsubscribed: false clears it. Unlike the other origins it covers marketing mail only (see below).
  • Manual — you added the address, in the dashboard or via the API.

Bounces and complaints arrive as SES events and suppress the address automatically. Suppressions are per team.

What suppression does

  • Transactional sends (POST /emails and the SMTP relay, without a topic_id): recipients suppressed for a bounce, a complaint or a manual entry are stripped from to/cc/bcc. If every to recipient is suppressed, the send is rejected with 422 all_recipients_suppressed (message All recipients are suppressed). An unsubscribe entry does not apply here: someone who opted out of marketing still receives password resets, receipts and other account mail — the same meaning Resend gives unsubscribed ("unsubscribed from all Broadcasts").
  • Topic sends (POST /emails with a topic_id) and broadcasts: every entry applies, unsubscribes included, plus the recipient's opt-out of that topic; broadcasts skip such contacts during fan-out.

A list imported with origin: "unsubscribe" therefore blocks topic sends and broadcasts only. Import with manual to block every send.

Every change to the list publishes a suppression.added or suppression.removed webhook event.

Reviewing and removing

The dashboard lists every suppressed address with the reason and date. You can remove an address to allow sending again — do this only when you know the cause is fixed (e.g. a mailbox that existed all along but was rejected by a misconfigured server). Re-suppression is automatic on the next bounce or complaint.

SES's own suppression list

Besides this per-team list, Amazon SES keeps an account-level suppression list per region, shared by every team on the instance. The setup wizard sets it to bounces only: a hard-bounced mailbox is dead for everyone, so SES may refuse it account-wide, but a spam report is about one sender's mail and stays on that team's list here. A send SES refuses because of its own list shows as a permanent bounce with the subtype OnAccountSuppressionList, and only the SES console can remove such an entry.

API

The /suppressions endpoints mirror Resend's suppressions surface, so the Resend SDK's suppressions.* methods work as-is. Each entry reads as { id, email, origin, source_id, created_at }, where origin is bounce, complaint, manual or unsubscribe and source_id is the email whose bounce or complaint created it.

  • GET /suppressions?origin=bounce — keyset-paginated list, optionally filtered by origin.
  • GET /suppressions/{id} and DELETE /suppressions/{id} — the path segment is the suppression id or the email address.
  • POST /suppressions with { "email": "...", "origin": "manual" } — blocks the address. origin is optional (bounce, complaint, manual or unsubscribe, default manual) and lets an import from another provider keep its bounce and complaint history, or a migrated opt-out list keep its reason. Idempotent: an address already suppressed for any reason keeps its entry and origin, and its existing id is returned.
  • POST /suppressions/batch/add with { "emails": [...], "origin": "bounce" } and POST /suppressions/batch/remove with { "emails": [...] } or { "ids": [...] } — up to 1000 entries per call (Resend caps at 100). Add applies the one optional origin to every row it creates and returns one id per distinct address in input order; remove lists only the rows actually removed.

Three MillionSend specifics: origin on add is accepted (Resend's SDK type has no such field, so pass it through a raw request), origin: "unsubscribe" is a superset value Resend does not have (its SDK's type union lacks it) and behaves like a one-click opt-out — only an explicit re-subscribe of the contact clears it — and an address whose personal data was erased (GDPR/LGPD) keeps blocking sends but is hidden from the list and from lookups by email — it is reachable by id only, reading "[erased]" as its email, and re-suppressing the address returns that id without restoring it.

Why this matters

SES tracks bounce and complaint rates and pauses senders that cross its thresholds. MillionSend's metrics page tracks your rates against those thresholds, and broadcast sending is blocked automatically when a rate crosses the pause line.

On Cloud, sending volume is governed by your plan's limits. Self-hosted, the limits are your own AWS SES account's quotas and reputation — crossing SES's thresholds can pause the whole account, which is exactly what suppression protects you from.

On this page