Deliverability

Suppressions API

5 endpoints under https://api.mailneo.co/api/v2. Every request carries your key in the x-api-key header.

Scopes used on this page: suppressions:delete, suppressions:read, suppressions:write. Each endpoint below states the one it requires. Start with the quickstart →

GET/suppressions

List suppressed addresses

Requires suppressions:read

The addresses this team must not mail. Cursor-paginated. Pass updated_since with the synced_through value from your previous run to fetch only what changed.

This list is complete for your team. Mirror it into your own sender if you send outside Mailneo.

Query parameters7
limitintegerdefault: 251..100
cursorstring≤ 256 chars
Opaque page cursor from a previous response’s next_cursor. Treat it as a black box: cursors are bound to the sort they were issued under and are validated strictly, so a hand-crafted, modified or foreign cursor — including any cursor issued before the 2026-08 format revision — answers 400 cursor_invalid. On that error, restart from the first page.
order"asc" | "desc"default: desc
updated_sincestring · date-time
sort"updated_at" | "created_at"default: updated_at
emailstring≤ 320 chars
reasonstring≤ 200 chars
curl "https://api.mailneo.co/api/v2/suppressions?limit=25&order=desc&sort=updated_at" \
  -H "x-api-key: $MAILNEO_API_KEY"
{
  "data": [
    {
      "object": "suppression",
      "id": "c0kqabhu0l09x6t0k6b43w0kg",
      "email": "ada@example.com",
      "reason": "manual",
      "created_at": "2026-05-11T09:30:00.000Z",
      "updated_at": "2026-06-24T15:45:00.000Z"
    }
  ],
  "meta": {
    "request_id": "8c0b1c47-8b0b-4ab4-ae0b-1f6d8d0b1dda",
    "has_more": false,
    "next_cursor": null,
    "synced_through": "2026-06-24T15:45:00.000Z"
  }
}
POST/suppressions

Add addresses to the suppression list

Requires suppressions:write · Idempotency-Key optional

Suppresses up to 100 addresses per call. Every send path consults this list, so an address added here stops receiving campaign and newsletter mail from this team immediately.

Suppression is an overlay, not a deletion: contacts and subscribers with these addresses are NOT deleted or modified, their list memberships survive, and removing the suppression later restores mailability (except where a per-list opt-out also applies). To erase a person, use the contact delete or the account data-erasure flow — this endpoint only blocks sending.

Partial novelty is the normal outcome and the response says which addresses landed where: added, or already_suppressed (left completely untouched — the existing record keeps its original reason, so a bounce or complaint record is never overwritten by a manual re-post, and its updated_at does not move). Re-posting the same batch is therefore always safe, which is what a retried compliance sync needs. Addresses are normalised (trimmed and lower-cased) before anything else happens.

reason accepts only MANUAL. The other values you will see on reads (bounce:hard, bounce:complaint, unsubscribe variants) are written by the delivery pipeline about its own observations and cannot be set through the API.

suppression.added webhook events are not emitted for additions made through this endpoint.

Query parameters1
dry_runboolean
Report what this call WOULD do and commit nothing. Returns data: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.
Header parameters1
Idempotency-Keystring≤ 255 chars
A caller-chosen unique string, at most 255 characters. Retrying with the same key replays the first response instead of repeating the work; the replay carries Idempotent-Replayed: true. Reusing a key with a different body is a 409 idempotency_key_reused. Keys are remembered for 24 hours.
Body parameters2
emailsstring[]required1..100 items
reason"MANUAL"default: MANUAL
curl -X POST "https://api.mailneo.co/api/v2/suppressions" \
  -H "x-api-key: $MAILNEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "emails": [
      "ada@example.com"
    ],
    "reason": "MANUAL"
  }'
{
  "data": {
    "object": "suppression_batch",
    "added": [
      "…"
    ],
    "already_suppressed": [
      "…"
    ]
  },
  "meta": {
    "request_id": "22693d00-2369-4e93-a469-4026256941b9",
    "dry_run": false
  }
}
GET/suppressions/{email}

Check whether an address is suppressed

Requires suppressions:read

Returns the suppression record for an address, or 404 if it is not suppressed. This is the check to run before mailing someone from your own system. The address must be URL-encoded.

Path parameters1
emailstringrequired3..320 chars
curl "https://api.mailneo.co/api/v2/suppressions/{email}" \
  -H "x-api-key: $MAILNEO_API_KEY"
{
  "data": {
    "object": "suppression",
    "id": "c0kqabhu0l09x6t0k6b43w0kg",
    "email": "ada@example.com",
    "reason": "manual",
    "created_at": "2026-05-11T09:30:00.000Z",
    "updated_at": "2026-06-24T15:45:00.000Z"
  },
  "meta": {
    "request_id": "ec7e1150-ed7e-42e3-ae7e-1476ef7e1609"
  }
}
GET/unsubscribes

List unsubscribe events

Requires suppressions:read

The unsubscribe events behind the suppression list — who opted out, from where, and from which campaign. Cursor-paginated.

updated_since is not supported here: the underlying record has no modification timestamp. Unsubscribes are append-only, so page by unsubscribed_at and keep the newest value you have seen as your high-water mark.

Query parameters7
limitintegerdefault: 251..100
cursorstring≤ 256 chars
Opaque page cursor from a previous response’s next_cursor. Treat it as a black box: cursors are bound to the sort they were issued under and are validated strictly, so a hand-crafted, modified or foreign cursor — including any cursor issued before the 2026-08 format revision — answers 400 cursor_invalid. On that error, restart from the first page.
order"asc" | "desc"default: desc
sort"unsubscribed_at" | "created_at"default: unsubscribed_at
emailstring≤ 320 chars
sourcestring≤ 100 chars
campaign_idstring≤ 64 chars
curl "https://api.mailneo.co/api/v2/unsubscribes?limit=25&order=desc&sort=unsubscribed_at" \
  -H "x-api-key: $MAILNEO_API_KEY"
{
  "data": [
    {
      "object": "unsubscribe",
      "id": "c14ms8qw14wrufv156rg4u15g",
      "email": "ada@example.com",
      "reason": "manual",
      "source": "api",
      "campaign_id": "c1j7okz31ixoza41jrnsd11jh",
      "unsubscribed_at": "2026-06-24T15:45:00.000Z",
      "created_at": "2026-05-11T09:30:00.000Z"
    }
  ],
  "meta": {
    "request_id": "b0ea5111-afea-4f7e-aeea-4debadea4c58",
    "has_more": false,
    "next_cursor": null
  }
}
DELETE/suppressions/{suppressionId}

Remove an address from the suppression list (un-suppress)

Requires suppressions:delete

Removes one suppression record, RE-ENABLING mail to an address that was deliberately blocked: every campaign and newsletter send path in this team will treat the address as mailable again the moment this returns. There is no undo beyond re-adding the suppression — call with ?dry_run=true first.

Check the record's reason before deleting. MANUAL rows were typed in by a person; bounce:hard, bounce:soft and bounce:complaint rows were recorded by the delivery pipeline, and UNSUBSCRIBED/unsubscribe rows record an opt-out. Removal is allowed whatever the reason — the same rule as the in-app suppression screen — but removing a bounce- or complaint-sourced record does not fix what caused it, and the pipeline will re-create the suppression automatically if the condition recurs (another hard bounce, another complaint). Un-suppressing an opt-out and then mailing the address is a compliance decision that is yours, not the platform's.

Only the suppression record is removed. The unsubscribe EVENT history (GET /unsubscribes) is untouched, and a per-list opt-out (an UNSUBSCRIBED list membership) still stands and still blocks that list's sends — this endpoint lifts the team-wide block, nothing else.

Find the id via GET /suppressions or GET /suppressions/{email}. The response deliberately does not echo the address.

Path parameters1
suppressionIdstringrequired1..64 chars
Query parameters1
dry_runboolean
Report what this call WOULD do and commit nothing. Returns data: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.
curl -X DELETE "https://api.mailneo.co/api/v2/suppressions/{suppressionId}" \
  -H "x-api-key: $MAILNEO_API_KEY"
{
  "data": {
    "object": "suppression",
    "id": "c0kqabhu0l09x6t0k6b43w0kg",
    "deleted": true
  },
  "meta": {
    "request_id": "50580dc1-4f58-4c2e-ae58-0a9b4d580908",
    "dry_run": false
  }
}