PristineSend
Get started
API Reference

Suppressions

The suppression list is your workspace's email-keyed do-not-send list. A suppressed address is blocked across every send path — transactional and campaigns alike — so you never mail it again. List the entries, add your own, and remove ones that no longer apply.

Overview

MethodPathDescription
GET/api/v1/suppressionsList suppressed addresses (cursor-paginated)
POST/api/v1/suppressionsAdd an address to the do-not-send list
DELETE/api/v1/suppressionsRemove a suppression ({ email, force? } body, or %40-encoded ?email=)

What suppression is

A suppression is a single email address on a do-not-send list, keyed by the address itself (not a contact). When an address is suppressed, PristineSend refuses to send to it on both paths:

  • Transactional POST /send and /send/batch reject a suppressed recipient with 403 recipient_suppressed. In a batch this surfaces as a per-item recipient_suppressed error (that item fails; the rest still send) rather than a whole-request 403.
  • Campaigns and automations — suppressed addresses are skipped, so they are never enqueued.

Addresses land on the list from several sources:

sourceMeaning
apiAdded explicitly via POST /api/v1/suppressions.
hard_bounceAuto-added after a permanent bounce — the mailbox is invalid or rejecting.
complaintAuto-added after a spam complaint — the recipient marked your mail as spam.
verificationAdded during address verification.
manualAdded by an operator in the dashboard.

The source matters when you remove a row: hard_bounce and complaint suppressions are protected and require force=true to delete (see Remove a suppression).

The suppression object

Each entry in the list has this shape:

FieldTypeDescription
emailstringThe suppressed email address — the key of the do-not-send list.
reasonstring | nullFree-text detail for the suppression (e.g. the bounce diagnostic), or null.
sourcestringHow the address was suppressed: api, hard_bounce, complaint, verification, or manual.
created_atstringISO 8601 timestamp the address was suppressed.

List suppressions

GET/api/v1/suppressions

Returns a cursor-paginated list of suppressed addresses, newest first, in the same { data, has_more, next_cursor } shape as the other list endpoints. Pass next_cursor back as the cursor parameter to fetch the next page.

Query parameters

FieldTypeRequiredDescription
emailstringoptionalLook up one exact address instead of listing. Returns a single-element data array if that address is suppressed, or an empty array if it is clear to send — the cheapest "is this address suppressed?" check. A malformed address returns 400 invalid_field.
limitnumberoptionalMax entries to return (ignored when email is set). Default 50, max 100 (over-large values are clamped).
cursorstringoptionalOpaque pagination cursor from a previous response's next_cursor. Omit for the first page; a malformed cursor returns 400 invalid_field.

Check one address: pass ?email= to look up a single address instead of paging the whole list — the fastest way to answer “is this recipient suppressed?” before a send. It returns the one matching entry (or an empty data array if the address is clear). This is the same check the MCP check_suppression tool runs.

Response

"color:#ff7b72">import { PristineSend } "color:#ff7b72">from "pristinesend"

"color:#ff7b72">const ps = "color:#ff7b72">new PristineSend(process.env.PRISTINESEND_API_KEY!)

// The returned page auto-paginates — iterate across every page transparently.
for "color:#ff7b72">await ("color:#ff7b72">const entry of "color:#ff7b72">await ps.suppressions.list({ limit: 100 })) {
  console.log(entry.email, entry.reason, entry.source)
}

Add a suppression

POST/api/v1/suppressions

Adds an address to the do-not-send list with source: "api". The email need not be an existing contact — you can suppress any address. The call is idempotent: re-POSTing an address that is already suppressed is a 200 no-op success, not a conflict. Returns 200 with the suppression object.

Request body

FieldTypeRequiredDescription
emailstringrequiredAddress to add to the do-not-send list. Need not be an existing contact. Added with source 'api'.
reasonstringoptionalFree-text note for why the address was suppressed. Stored as-is; omit for null.

Response

"color:#ff7b72">import { PristineSend } "color:#ff7b72">from "pristinesend"

"color:#ff7b72">const ps = "color:#ff7b72">new PristineSend(process.env.PRISTINESEND_API_KEY!)

// Idempotent: re-adding the same address is a no-op success, not an error.
"color:#ff7b72">const entry = "color:#ff7b72">await ps.suppressions.create({
  email: "optout@example.com",
  reason: "User asked to be removed",
})
console.log("Suppressed:", entry.email, entry.source)

Remove a suppression

DELETE/api/v1/suppressions

Removes the suppression for the given email, letting mail to that address resume. Send the address in the JSON body ({ "email": "…" }) — recommended, since a raw @ in the URL is rejected by the edge with a 404 before it reaches the API. You may instead pass ?email= in the query, but the @ must be percent-encoded as %40. Returns 204 No Content on success, or 404 not_found if the address is not suppressed.

force=true required for bounces and complaints. A suppression with source: "hard_bounce" or source: "complaint" is protected — deleting it without &force=true returns 409 conflict. These addresses bounced or filed a spam complaint, so removing the suppression resumes mail to a known-bad recipient and can hurt your sending reputation. Pass &force=true only when you are certain. api, manual, and verification rows are removed freely without the flag.

Parameters (body or query)

FieldTypeRequiredDescription
emailstringrequiredThe suppressed address to remove. Send it in the JSON body (recommended — keeps "@" out of the URL) or as a URL-encoded ?email= query param (encode "@" as %40).
forcebooleanoptionalSet to true to remove a hard_bounce- or complaint-sourced suppression. Required for those sources; ignored for api/manual/verification rows. Accepted in the body or the query.

Response

"color:#ff7b72">import { PristineSend, ConflictError } "color:#ff7b72">from "pristinesend"

"color:#ff7b72">const ps = "color:#ff7b72">new PristineSend(process.env.PRISTINESEND_API_KEY!)

// The SDK sends the address in the body, so "@" never hits the URL. Pass
// force: true to lift a hard_bounce- or complaint-sourced suppression.
try {
  "color:#ff7b72">await ps.suppressions.delete({ email: "bounced@example.com", force: true })
  // Resolves on 204 No Content — mail to this address resumes.
} catch (err) {
  if (err instanceof ConflictError) {
    // Bounced/complained — retry with force: true only if you really mean to.
  }
  throw err
}

Errors

All errors use the standard envelope. The codes specific to this resource:

StatuscodeWhen
400missing_fieldPOST without an email field.
400invalid_fieldAn invalid email, or a malformed cursor on list.
401unauthorizedMissing or invalid API key.
404not_foundDELETE for an address that is not suppressed.
409conflictDELETE of a hard_bounce/complaint suppression without force=true.

Separately, recipient_suppressed (403) is raised by the send endpoints — not this one — when you try to mail an address on this list. See the full Error codes reference for the canonical envelope and the complete list.