Guides

Suppressions and compliance

Why we refuse a send, and why you should not always override it.

A suppression is an address Mailyte will not send to. They are added automatically when something goes wrong, and they are enforced before your own list logic gets a say.

That last part surprises people, so it is worth stating plainly: removing someone from your database does not remove them from here, and adding them back does not resurrect them here.

Why an address gets suppressed

Reason Added when Reversible?
bounced A permanent delivery failure — no such mailbox, domain does not accept mail Sometimes
complained The recipient marked a message as spam Treat as never
unsubscribed The recipient used an unsubscribe link Only by them
manual You added it Yes

Reading the list

curl "$MAILYTE_BASE/api/v1/email-suppressions" \
  -H "Authorization: Bearer $MAILYTE_API_KEY"

Requires suppressions:read. Worth syncing into your own system so your UI can explain why a customer is not receiving mail, instead of showing "sent" against something that never went.

What a suppressed send looks like

POST /messages returns 422, and an email.dropped event is recorded.

The event matters. A suppressed send used to leave no trace at all — nothing in the log, nothing in the timeline, and the caller's own error the only evidence it ever existed. That is the hardest kind of missing mail to explain, because from the outside it is indistinguishable from a message nobody ever tried to send. Now the drop is on the record whether or not your code handles the error.

Importing an existing list

Moving from another provider? Bring your suppressions with you before your first send, not after.

curl -X POST "$MAILYTE_BASE/api/v1/email-suppressions/import" \
  -H "Authorization: Bearer $MAILYTE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "suppressions": [
      { "email": "old@example.com", "reason": "bounced" },
      { "email": "angry@example.com", "reason": "complained" }
    ]
  }'

Skipping this is the classic migration mistake. Your old provider spent months learning which addresses are bad; starting fresh means re-learning it by mailing every one of them again, from a domain with no reputation, in a single send. That is close to the worst possible first impression to make on a receiving server.

Removing one

curl -X DELETE "$MAILYTE_BASE/api/v1/email-suppressions/{id}" \
  -H "Authorization: Bearer $MAILYTE_API_KEY"

Reasonable when you know why it was suppressed and the cause is gone — a mistyped address that has been corrected, a mailbox that was deleted and recreated, someone who has genuinely asked to be resubscribed.

Not reasonable for a complaint. Someone pressed "this is spam". Sending to them again produces another complaint, and complaints are weighted heavily enough that a small number sustained will damage delivery for every message you send, to everyone.

Never bulk-clear the list. If you are tempted to, the problem is upstream — in how the addresses were collected — and clearing suppressions is the fastest available way to make it worse.

Unsubscribes are not optional

Marketing mail must carry a working unsubscribe. Campaigns add List-Unsubscribe headers and a link automatically.

Transactional mail must not carry one. A password reset with an unsubscribe link lets someone opt out of being able to log in, and — because those headers are how mail clients classify a message — it also teaches them to file your security mail as a newsletter. That is not theoretical: adding a tracking pixel and List-Unsubscribe to every HTML message once got our own password resets categorised as bulk mail by a major provider.

Keep the two streams separate. POST /messages is transactional; campaigns are marketing.

Next

Running a campaign — where unsubscribe handling is built in.