PosthasteDocsGet an API key

Sending

Suppressions

An address that hard bounces or files a complaint is suppressed immediately. Sending to a suppressed address returns 422 suppressed before anything is written — you find out at the API, not from a bounce an hour later.

Suppressions are per account

A suppression on your account stops your mail to that address. It does not stop anybody else’s: another Posthaste customer with a legitimate relationship with the same person is unaffected, which is correct — a bounce from your list says something about your list, not about the address in general.

There is a second, narrower kind. A platform suppression, marked "scope": "platform", applies to every account on the infrastructure. Those are added by us, for addresses that endanger the sending reputation everyone shares — spam traps, and addresses that have demonstrated the same. They appear in your list so that a 422 is explicable, and no account can remove one.

Reasons

ReasonMeaning
hard_bounceThe receiving server permanently refused the address — no such mailbox, no such domain. Added automatically, and by far the most common reason. Not removable through the API; support can lift a genuinely stale one after review.
complaintThe recipient reported the message as spam. Added automatically. Cannot be removed.
manualYou added it yourself through the API or the dashboard.
unsubscribeThe recipient used an unsubscribe mechanism we handled.
spam_trapThe address was identified as a spam trap. Sending to one is a fast route to a blocklist.

A temporary failure never suppresses. A mailbox that was full for an afternoon, greylisting, a rate limit at the receiving end — all of those are deferrals, retried on a backoff. Suppressing on a transient error would make a working recipient permanently unreachable, and you would never find out why.

List suppressions

GET/v1/suppressionssuppressions:read

Newest first, keyset-paginated. See pagination.

ParameterTypeNotes
limitinteger optional1–200, default 50.
beforestring optionalA sup_ id — the nextCursor from the previous page.
searchstring optional1–320 characters, matched anywhere in the address. People remember a domain, not a whole address.
reasonstring optionalOne of the five above, spelled exactly. Anything else returns 400 invalid_request.
// One page.
const page = await posthaste.suppressions.list({ reason: 'complaint', limit: 100 })

// Or every page.
for await (const entry of posthaste.suppressions.autoPaginate({ reason: 'hard_bounce' })) {
  entry.address
  entry.scope // 'account' — yours to remove — or 'platform', which is not
}

detail carries whatever explanation exists — for a bounce, the receiving server’s own diagnostic text. scope is account or platform, said here so you do not discover the difference from a 422 on delete.

Why an address is on the list

Every entry says what happened, in the receiving server’s own terms, and links back to the send that caused it. That is the part you can act on: an address on this list was almost always produced by a system you own, and the message it broke on is where to look.

FieldMeaning
reasonThe category — one of the five above.
detailThe receiving server’s own words, verbatim, up to 500 characters. For an entry you created, whatever note you sent as reason.
smtpStatusThe status it gave — an RFC 3463 enhanced status such as 5.1.1, or the reply code the conversation ended on. null where the report carried neither.
messageThe send that provoked it: id (a msg_ id you can fetch), subject, to and createdAt.

message and smtpStatus are null on some rows, and that is not an error. An address you suppressed by hand has no causing send. Neither has one whose message has since passed out of your retention window — a suppression deliberately outlives the evidence for it, because the address is still dead once the message is gone. Entries made before we started recording the cause have neither field.

Add one

POST/v1/suppressionssuppressions:write

For addresses you know you should not mail — an in-app opt-out, a customer request, an address your own system has retired.

FieldTypeNotes
addressstring required3–320 characters. Normalised before storage, so casing and equivalent forms match.
reasonstring optionalUp to 500 characters of free text. It is stored as the entry’s detail, not as its reason — a suppression you create always has reason manual. Write a note your future self can act on.
await posthaste.suppressions.create({
  address: '[email protected]',
  // Free text. Stored as the entry's DETAIL — the reason is always 'manual'.
  reason: 'asked to be removed by email',
})
StatusWhen
201{ address, reason: "manual" }. Adding an address that is already suppressed is a no-op and still returns 201 — the end state is what was asked for.
422 invalid_addressThe value is not an email address.
400 invalid_requestThe field is missing or out of range.

Remove one

DELETE/v1/suppressions/:addresssuppressions:write

The path segment is the address, URL-encoded — not a sup_ id. This route removes the entries you created: manual and unsubscribe. An opt-out the customer has since reversed is the case it is for.

It will not remove a hard_bounce. A permanent refusal is the receiving server telling you the mailbox does not exist, which is a fact about the address rather than about your mail — deleting our record of it does not recreate the mailbox, it just sends again, collects the same failure, and spends reputation that is shared. If you know a bounce is genuinely stale — the mailbox has been recreated, or a DNS misconfiguration on your side has been fixed — contact support. An operator can lift it after review, with the reason recorded; the customer API deliberately has no way to do it unreviewed.

// The ADDRESS, not a sup_ id. The SDK url-encodes it for you.
await posthaste.suppressions.delete('[email protected]')

// Only a 'manual' or 'unsubscribe' entry can be removed here. Refused with 422
// for a hard bounce (suppression_hard_bounce — ask support, it is reviewable),
// a complaint or spam trap (suppression_protected — never), or a platform-wide
// entry (suppression_platform).
StatusWhen
204Removed.
404 not_foundThat address is not suppressed on this account.
422 suppression_hard_bounceThe entry’s reason is hard_bounce. Not removable through the API. Contact support if you believe it is stale — an operator can remove it after review.
422 suppression_protectedThe entry’s reason is complaint or spam_trap. It can never be removed, by you or by us. Somebody marked your mail as spam; sending to them again is what gets a sending IP blocklisted, and the IP is shared.
422 suppression_platformA platform-wide entry. Not removable by an account. Contact support if you believe it is wrong.

Do not loop over your suppression list removing entries. It is the single fastest way to damage your own delivery. Fix hard bounces at the source — a list that keeps producing them is a list with a collection problem, and re-mailing it makes the next bounce rate worse, not better.

NextSending limits →The monthly allowance, your account’s daily cap, and how the cap moves.