Core

Subscribers API

5 endpoints under https://api.mailneo.co/api/v2. Every request carries your key in the X-API-Key header. The scopes used here: subscribers:delete, subscribers:read, subscribers:write. Each endpoint below states the one it requires. Start with the guide →

Subscribers
listSubscribers

List newsletter subscribers

Cursor-paginated. Pass updated_since with the synced_through value from your previous run to fetch only what changed; you will occasionally re-see a row, so upsert by id. Deletes are not visible to updated_since.

Subscribers are distinct from contacts: a contact is a cold-outreach record, a subscriber is on a newsletter list. Creating one does not create the other.

https://api.mailneo.co/api/v2
GET/subscriberssubscribers:read

Example request

curl "https://api.mailneo.co/api/v2/subscribers?limit=25&order=desc&sort=updated_at" \
  -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
Required scope
subscribers:read. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Query parameters10
NameTypeRequiredNotes
limitintegerNodefault 25 · 1–100
cursorstringNomax 256 chars
order"asc" | "desc"Nodefault "desc"
updated_sincestring · date-timeNoISO 8601
sort"updated_at" | "created_at"Nodefault "updated_at"
status"ENABLED" | "DISABLED" | "BLOCKLISTED"No
emailstringNomax 320 chars
sourcestringNomax 200 chars
tagsstringNomax 1000 chars
list_idstringNomax 64 chars
Response · 200

Success

data[]Each element of the array.

FieldTypeAlways presentNotes
object"subscriber"Yes
idstringYes
emailstringYes
namestringYes
first_namestring · nullableYes
last_namestring · nullableYes
phonestring · nullableYes
companystring · nullableYes
statusstringYes
tagsstring[]Yes
sourcestring · nullableYes
custom_fieldsobjectYesopen keys
last_contacted_atstring · nullableYes
created_atstringYes
updated_atstringYes

meta

FieldTypeAlways presentNotes
request_idstringYes
has_morebooleanYes
next_cursorstring · nullableYes
synced_throughstringNo
Subscriberswrite
createSubscriber

Create a subscriber

Creates one subscriber. The email is normalised (trimmed and lower-cased) and must be unique within the team — a duplicate is a 409 resource_already_exists, not a silent upsert, unlike the v1 endpoint this replaces. When name is omitted it is composed from first_name and last_name, matching what the app does.

status accepts ENABLED or DISABLED. BLOCKLISTED cannot be written in either direction: it is the stored record of a global opt-out, owned by the unsubscribe and suppression machinery.

The new subscriber is on NO list — attach them with POST /lists/{listId}/subscribers, which requires lists:write and lets the list's own opt-in setting decide how the subscription starts. Subscribers are distinct from contacts: creating one does not create the other.

Automations do not run on subscribers created this way: a "subscriber is created" trigger will not fire, and no webhook is emitted.

https://api.mailneo.co/api/v2
POST/subscriberssubscribers:write

Example request

curl -X POST "https://api.mailneo.co/api/v2/subscribers" \
  -H "X-API-Key: $MAILNEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "<string>"
  }'
Authorization
Required scope
subscribers:write. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Idempotency
Idempotency-Key is optional.
Query parameters1
NameTypeRequiredNotes
dry_runbooleanNoReport 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
NameTypeRequiredNotes
Idempotency-KeystringNoA 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. · max 255 chars
Request body

Body

FieldTypeAlways presentNotes
emailstringYesmax 320 chars · pattern-constrained
namestringNomax 200 chars
first_namestring · nullableNomax 200 chars
last_namestring · nullableNomax 200 chars
phonestring · nullableNomax 50 chars
companystring · nullableNomax 200 chars
sourcestring · nullableNomax 200 chars
status"ENABLED" | "DISABLED"No
tagsstring[]No
custom_fieldsobjectNoopen keys
Response · 200

Success

data

FieldTypeAlways presentNotes
dataobject | object · nullableYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes
Response · 201

Created

data

FieldTypeAlways presentNotes
dataobject | object · nullableYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes
Subscribers
getSubscriber

Retrieve a subscriber

https://api.mailneo.co/api/v2
GET/subscribers/{subscriberId}subscribers:read

Example request

curl "https://api.mailneo.co/api/v2/subscribers/{subscriberId}" \
  -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
Required scope
subscribers:read. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Path parameters1
NameTypeRequiredNotes
subscriberIdstringYesmax 64 chars
Response · 200

Success

data

FieldTypeAlways presentNotes
object"subscriber"Yes
idstringYes
emailstringYes
namestringYes
first_namestring · nullableYes
last_namestring · nullableYes
phonestring · nullableYes
companystring · nullableYes
statusstringYes
tagsstring[]Yes
sourcestring · nullableYes
custom_fieldsobjectYesopen keys
last_contacted_atstring · nullableYes
created_atstringYes
updated_atstringYes

meta

FieldTypeAlways presentNotes
request_idstringYes
Subscriberswrite
updateSubscriber

Update a subscriber

Merge-patch: fields you omit are left alone, and null clears a nullable field (name is not nullable — clear it with ""). tags REPLACES the whole array — v1 merged them, this does not — while custom_fields merges key by key so a value written by an import is not lost; clear one custom field by sending it as null.

Changing email is allowed and must not collide with another subscriber in the team (409 resource_already_exists).

A BLOCKLISTED subscriber — someone who globally opted out — keeps that status: sending status for them is a 409 resource_state_invalid, because re-enabling an opt-out is not a routine write. Their other fields can still be edited.

List membership is not editable here — that is POST /lists/{listId}/subscribers under lists:write. Field-update automations do not run on edits made this way.

https://api.mailneo.co/api/v2
PATCH/subscribers/{subscriberId}subscribers:write

Example request

curl -X PATCH "https://api.mailneo.co/api/v2/subscribers/{subscriberId}" \
  -H "X-API-Key: $MAILNEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "<string>",
    "name": "<string>",
    "first_name": "<string>",
    "last_name": "<string>",
    "phone": "<string>",
    "company": "<string>",
    "source": "<string>",
    "status": "ENABLED",
    "tags": [
      "<string>"
    ],
    "custom_fields": {}
  }'
Authorization
Required scope
subscribers:write. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Idempotency
Idempotency-Key is optional.
Path parameters1
NameTypeRequiredNotes
subscriberIdstringYesmax 64 chars
Query parameters1
NameTypeRequiredNotes
dry_runbooleanNoReport 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
NameTypeRequiredNotes
Idempotency-KeystringNoA 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. · max 255 chars
Request body

Body

FieldTypeAlways presentNotes
emailstringNomax 320 chars · pattern-constrained
namestringNomax 200 chars
first_namestring · nullableNomax 200 chars
last_namestring · nullableNomax 200 chars
phonestring · nullableNomax 50 chars
companystring · nullableNomax 200 chars
sourcestring · nullableNomax 200 chars
status"ENABLED" | "DISABLED"No
tagsstring[]No
custom_fieldsobjectNoopen keys
Response · 200

Success

data

FieldTypeAlways presentNotes
dataobject | object · nullableYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes
Subscribersdestructive
deleteSubscriber

Delete a subscriber

Permanent, and there is no undo — call with ?dry_run=true first.

The subscriber row is removed and their engagement trail goes with it: list memberships cascade — INCLUDING any per-list unsubscribe records, so re-creating the same address and re-adding it to a list would mail someone who had left that list — and open/click tracking rows are deleted. The send log is kept for campaign analytics with its email anonymised in the same transaction.

A GLOBAL opt-out survives: the suppression list is keyed by email address, not by subscriber, and deleting the subscriber does not touch it. This is also not a complete GDPR erasure for the person — the address may still appear as a contact or in the inbox; use the account data-erasure flow for that.

https://api.mailneo.co/api/v2
DELETE/subscribers/{subscriberId}subscribers:delete

Example request

curl -X DELETE "https://api.mailneo.co/api/v2/subscribers/{subscriberId}" \
  -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
Required scope
subscribers:delete. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Path parameters1
NameTypeRequiredNotes
subscriberIdstringYesmax 64 chars
Query parameters1
NameTypeRequiredNotes
dry_runbooleanNoReport 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.
Response · 200

Success

dataMay be null.

FieldTypeAlways presentNotes
object"subscriber"Yes
idstringYes
deletedtrueYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes

Next Steps