Docs/Sending email

Suppressions

A suppression is an address Anypost will not deliver to. The list is your team's, and it protects your sending reputation by stopping mail to addresses that bounced, complained, or opted out.

How an address gets suppressed

Most suppressions are added automatically. You can also add one yourself.

reasonAdded when
permanent_bounceA receiving server permanently rejected delivery, usually a non-existent address.
complaintA recipient marked a message as spam.
unsubscribedA recipient used a one-click unsubscribe link, or opted out through their mailbox provider's feedback report.
manualYou added the address through the API or dashboard, or imported it from a file without a reason Anypost recognizes.

A complaint or a stream of bounces damages how mailbox providers see your domain. Suppressing those addresses on the first signal is what keeps the rest of your mail in the inbox.

Topic scope

Every suppression is scoped to a topic, and the scope decides which sends it blocks.

  • A global suppression has the topic *. It blocks every send to that address. Bounces and complaints always write a global suppression: a dead address is dead for all mail. So does an opt-out reported by a mailbox provider, which names no topic.
  • A topic-scoped suppression names one topic, such as marketing. It blocks only sends carrying that same topic. A one-click unsubscribe writes one of these, scoped to the topic the recipient unsubscribed from.

This scoping is the transactional carve-out. A recipient who unsubscribes from marketing is suppressed for marketing only, so a later password reset or one-time code, sent with a different topic or none at all, still reaches them. A send with no topic is checked against global suppressions only. See Tags, topics & campaigns.

What happens on a send to a suppressed address

When a send names an address that is suppressed for that send's topic, the address is dropped before delivery. The rest of the recipients are unaffected, and you are not billed for the dropped recipient. Anypost emits an email.suppressed event so the outcome is visible on your webhooks.

Listing suppressions

GET /v1/suppressions returns the list, newest first, with cursor pagination. The TypeScript list returns a page you can iterate to walk every page:

curl https://api.anypost.com/v1/suppressions \
  -H "Authorization: Bearer $ANYPOST_API_KEY"

Narrow the list with query parameters:

ParameterRestricts to
email_containsAddresses containing this substring.
topicOne topic. Pass * for global entries only.
reasonpermanent_bounce, complaint, unsubscribed, or manual.
originauto for automatic entries, manual for ones you added.

To check one address, GET /v1/suppressions/{email} returns every record on file for it, across all topics. The {email} segment is URL-encoded.

Adding a suppression

POST /v1/suppressions adds an address yourself, for example on a request from a customer:

curl https://api.anypost.com/v1/suppressions \
  -H "Authorization: Bearer $ANYPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "topic": "marketing",
    "note": "customer requested removal"
  }'

topic is optional and defaults to *, a global suppression. Set a specific topic to block only that stream. note is an optional internal annotation. Adding an address that is already suppressed for the same (email, topic) pair returns 422.

Adding in bulk

POST /v1/suppressions/batch adds up to 1,000 addresses per call. Each item takes the same email, topic and note as a single add, plus an optional reason that maps the same way as an import's reason column:

curl https://api.anypost.com/v1/suppressions/batch \
  -H "Authorization: Bearer $ANYPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "suppressions": [
      { "email": "[email protected]", "reason": "hard bounce" },
      { "email": "[email protected]", "topic": "marketing", "reason": "unsubscribed" }
    ]
  }'
{ "imported": 2, "already_suppressed": 0, "invalid": [] }

Unlike a single add, an address already suppressed for that topic is counted in already_suppressed and left unchanged instead of returning 422, so a batch is safe to retry. An email that is not a valid address is listed in invalid with its position; the rest of the batch still goes through. A batch is not an import: it has no undo, so remove entries with DELETE. A batch that would take you past your suppression limit returns 422 and adds nothing.

Importing a list

Moving from another provider? Bring its suppression list with you, so the first send through Anypost does not mail addresses that already bounced, complained, or unsubscribed there.

  1. Export the list from your previous provider as CSV. Export bounces and complaints together if you can; export each unsubscribe list on its own.
  2. In the dashboard, open Suppressions and choose Import.
  3. Pick the file. Anypost reads the column headed email (or email_address, address, recipient), or the first column holding addresses. A plain file with one address per line works too. An optional column headed reason keeps each address's reason (see below).
  4. Leave Topic blank for bounces and complaints. For an unsubscribe list from one mailing list, set the topic that list maps to, so the rest of your mail still reaches those recipients.

Keep the tab open until the import finishes. One import takes up to 1,000,000 rows; for a larger list, contact [email protected]. Imported addresses count toward your suppression limit. An import that would go past it stops at that point and keeps what it added before.

ResultMeaning
Added to your listSuppressed now, with the reason from the file, or manual.
Already suppressedAlready on your list for that topic, or repeated in the file. The existing entry is unchanged.
Not a valid addressSkipped.

The reason column takes our values (permanent_bounce, complaint, unsubscribed, manual) and the words other providers export, ignoring case, spaces and punctuation:

File saysImported as
bounce, bounced, hard bouncepermanent_bounce
complaint, complained, spam complaint, spam reportcomplaint
unsubscribe, unsubscribed, opt-outunsubscribed
Blank, or anything else (an SMTP response, soft bounce)manual

Every imported address is suppressed whatever its reason; the reason only labels it in your list and in email.suppressed events.

Imported addresses are enforced within 30 seconds. Recent imports on the same page lists each import with its counts. Undo removes the addresses that import added, and is available for 7 days after the import starts. Addresses that were already suppressed before the import, or that bounced or complained after it, stay suppressed. To reverse an older import, contact [email protected].

Limits

Each team can hold as many suppressions it adds itself (by import, the API, or the dashboard) as its plan sends in a month, and at least 10,000. A team on a 1,000,000-email plan can add 1,000,000. The count covers every reason you set, including bounces and complaints you import. Suppressions Anypost adds from your own sending (bounces, complaints, and unsubscribes) never count, so they never stop.

The Suppressions page shows how much of the limit you use. Removing a suppression or undoing an import frees room. An add that would go past the limit returns 422 and adds nothing. Upgrade your plan, or contact [email protected] to raise the limit.

Removing a suppression

Removing an address makes it eligible to receive mail again on the next send.

  • DELETE /v1/suppressions/{email} removes the address across every topic.
  • DELETE /v1/suppressions/{email}/{topic} removes one topic's entry and leaves the others. Use %2A for the global topic *.

Removal does not stop a fresh suppression later: a future bounce, complaint, or unsubscribe for that address writes a new record. Lift a suppression only when you have reason to believe the address is good again.

Expiry

Suppressions do not expire. Every entry, whatever its reason, stays enforced until you remove it.

Where to go next