Suppressions

How addresses land on your suppression list, what happens when you send to them, and how to list, add, and remove entries.

A suppression is an address Mailfully will not deliver to. Entries are created automatically from hard bounces and spam complaints, or manually. The list is org-global: one list covers both your test and live environments.

What gets suppressed automatically

TriggerReason recorded
Permanent (hard) bouncehard_bounce
Spam complaintcomplaint

Transient and undetermined bounces never suppress. A soft bounce adds no entry, changes no message status, and emits no webhook. Mailfully stores every hard-bounced or complained address lowercased and mirrors each entry to the underlying SES account-level suppression list.

Sending to a suppressed address

Sends to suppressed addresses are not rejected. You get no error. At accept time, suppressed recipients are silently dropped from to, cc, and bcc:

  • Some recipients suppressed: the message sends to the remaining recipients.
  • All to recipients suppressed: you still get 202 and a message id, but the message is persisted with status canceled. Nothing is sent, and the message is never metered.

A race window also exists: an address can be suppressed between accept and send (for example, by a hard bounce landing moments earlier). A pre-send guard re-checks every recipient immediately before dispatch. If all to recipients are suppressed by then, the message flips to status failed with last_event: "suppressed", and a synthetic suppressed event listing every dropped recipient is written to the message's event timeline.

A 202 means Mailfully accepted the request, not that mail went out. If you need to know whether a message was actually sent, poll GET /v1/emails/:id for its status, or subscribe to webhooks.

List and filter suppressions

Listing requires the read:suppressions scope, which can be minted onto an API key. An mf_ key works here.

curl "https://api.mailfully.com/v1/suppressions?email=ada@example.com" \
  -H "Authorization: Bearer mf_live_xxxxxxxxxxxx"
{
  "data": [
    {
      "id": "01J1F5S8ZG9Q2W7X4V3B2N1M0K",
      "email": "ada@example.com",
      "reason": "hard_bounce",
      "source": "ses_notification",
      "created_at": "2026-07-08T14:23:07.123Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Query parameters:

  • email: exact match. Stored values are canonical lowercase, so lowercase your query or it will not match.
  • reason: exact match against hard_bounce, complaint, or manual.
  • limit: default 25, max 100.
  • cursor: opaque cursor from a previous page's next_cursor.

See List suppressions for the full schema.

Add a suppression manually

curl -X POST https://api.mailfully.com/v1/suppressions \
  -H "Authorization: Bearer <session token>" \
  -H "Content-Type: application/json" \
  -d '{ "email": "ada@example.com" }'
{
  "id": "01J1G0Q4RS7T9V2W5X8Y1Z3A6B",
  "email": "ada@example.com",
  "reason": "manual",
  "source": "dashboard",
  "created_at": "2026-07-08T15:01:44.502Z"
}

Adding and removing entries needs the session-only manage:suppressions scope, so an mf_ key gets 403 insufficient_scope here. See Authentication.

The add is idempotent: a new entry returns 201, and adding an address that is already suppressed returns 200 with the existing entry. The email is trimmed and lowercased before storing. See Create suppression.

Remove a suppression

curl -X DELETE https://api.mailfully.com/v1/suppressions/01J1F5S8ZG9Q2W7X4V3B2N1M0K \
  -H "Authorization: Bearer <session token>"
{
  "id": "01J1F5S8ZG9Q2W7X4V3B2N1M0K",
  "deleted": true
}

When the entry was synced to SES, Mailfully also clears the SES-level block if no other suppression still covers that address, then deletes the entry. The call fails closed: if SES cannot be updated, the request returns 503 service_unavailable and the entry stays intact. Retry the delete. See Delete suppression.

Think twice before removing a hard_bounce entry. The address permanently rejected your mail; sending to it again will most likely hard-bounce again, and the next permanent bounce re-suppresses it. Remove hard-bounce entries only when you know the mailbox has changed — a typo fix, a re-created account.

Reason values

ReasonWritten bySource value
hard_bouncePermanent bounceses_notification
complaintSpam complaintses_notification
manualPOST /v1/suppressionsdashboard