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 →
/suppressionsList 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..100cursorstring≤ 256 charsnext_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: descupdated_sincestring · date-timesort"updated_at" | "created_at"default: updated_atemailstring≤ 320 charsreasonstring≤ 200 charscurl "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"
}
}/suppressionsAdd 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_runbooleandata: 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 charsIdempotent-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 itemsreason"MANUAL"default: MANUALcurl -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
}
}/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 charscurl "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"
}
}/unsubscribesList 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..100cursorstring≤ 256 charsnext_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: descsort"unsubscribed_at" | "created_at"default: unsubscribed_atemailstring≤ 320 charssourcestring≤ 100 charscampaign_idstring≤ 64 charscurl "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
}
}/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 charsQuery parameters1
dry_runbooleandata: 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
}
}