Core
Segments API
6 endpoints under https://api.mailneo.co/api/v2. Every request carries your key in the X-API-Key header. The scopes used here: segments:read, segments:write. Each endpoint below states the one it requires. Start with the guide →
listSegmentsList segments
Saved subscriber filters. Cursor-paginated; pass updated_since with the synced_through value from your previous run to fetch only what changed.
The stored filter definition is not published — see the segment object. Use GET /segments/{segmentId}/count for the number of subscribers a segment matches.
/segmentssegments:readExample request
curl "https://api.mailneo.co/api/v2/segments?limit=25&order=desc&sort=updated_at" \ -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
- Required scope
segments:read. A key without it is refused before the endpoint runs.- Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Query parameters6
| Name | Type | Required | Notes |
|---|---|---|---|
| limit | integer | No | default 25 · 1–100 |
| cursor | string | No | max 256 chars |
| order | "asc" | "desc" | No | default "desc" |
| updated_since | string · date-time | No | ISO 8601 |
| sort | "updated_at" | "created_at" | No | default "updated_at" |
| name | string | No | max 200 chars |
Response · 200
Success
data[]Each element of the array.
| Field | Type | Always present | Notes |
|---|---|---|---|
| object | "segment" | Yes | |
| id | string | Yes | |
| name | string | Yes | |
| created_at | string | Yes | |
| updated_at | string | Yes |
meta
| Field | Type | Always present | Notes |
|---|---|---|---|
| request_id | string | Yes | |
| has_more | boolean | Yes | |
| next_cursor | string · nullable | Yes | |
| synced_through | string | No |
createSegmentCreate a segment
Creates one saved subscriber filter. Names are NOT required to be unique — the app does not enforce it either — so two segments may share a name and are told apart by id.
The conditions definition is a strict, validated language, not free-form JSON: a group (operator: AND/OR) of conditions, nestable two levels. Each condition names a field, an operator and usually a value. Supported fields and their operators:
- email, name, firstName, lastName — equals, not_equals, contains, not_contains, starts_with, ends_with; is_set/is_not_set on firstName/lastName only (the other two always have a value).
- status — equals, not_equals, in, not_in over ENABLED, DISABLED, BLOCKLISTED.
- score — equals, not_equals, greater_than, less_than, greater_than_or_equals, less_than_or_equals, in, not_in; numbers only.
- tags — contains/not_contains (one tag), equals/not_equals (all of), in/not_in (any of), is_set/is_not_set.
- lists — in/not_in over list ids, meaning a CONFIRMED subscription to any of them. Every id must be a list in your team.
- subscription_status — equals over UNCONFIRMED, CONFIRMED, UNSUBSCRIBED.
- createdAt, updatedAt, lastContactedAt — equals (whole day), before, after, between; ISO 8601 date strings.
- lastOpenedAt, lastClickedAt — before/after (newsletter open/click activity relative to a date).
- customFields.<path> or attributes.<path> — equals, not_equals, contains, is_set, is_not_set on a key inside the subscriber's custom-field JSON. Paths are not checked against a catalog (the keys are your own); a path no subscriber carries simply matches no one.
Counts of opens, clicks and bounces (opens_count and friends) are deliberately NOT accepted: the API cannot evaluate them, so a segment saved with one could not be counted by GET /segments/{segmentId}/count. Create those in the dashboard.
Everything this endpoint accepts is guaranteed evaluatable by the count endpoint. The stored definition is not echoed back and is not published on segment reads.
/segmentssegments:writeExample request
curl -X POST "https://api.mailneo.co/api/v2/segments" \
-H "X-API-Key: $MAILNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "<string>",
"conditions": {
"operator": "AND",
"conditions": [
"<string>"
]
}
}'Authorization
- Required scope
segments:write. A key without it is refused before the endpoint runs.- Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...- Idempotency
Idempotency-Keyis optional.
Query parameters1
| Name | Type | Required | Notes |
|---|---|---|---|
| dry_run | boolean | No | 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
| Name | Type | Required | Notes |
|---|---|---|---|
| Idempotency-Key | string | No | 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. · max 255 chars |
Request body
name
| Field | Type | Always present | Notes |
|---|---|---|---|
| name | string | Yes | max 200 chars |
conditions
| Field | Type | Always present | Notes |
|---|---|---|---|
| operator | "AND" | "OR" | Yes | |
| conditions | (object | object)[] | Yes |
Response · 200
Success
data
| Field | Type | Always present | Notes |
|---|---|---|---|
| data | object | object · nullable | Yes |
meta
| Field | Type | Always present | Notes |
|---|---|---|---|
| request_id | string | Yes | |
| dry_run | boolean | Yes | |
| impact | object | No | |
| impact.object | "impact" | Yes | |
| impact.would_create | integer | Yes | |
| impact.would_update | integer | Yes | |
| impact.would_delete | integer | Yes | |
| impact.affected_count | integer | Yes | |
| impact.affected_ids | string[] | Yes | |
| impact.quota | object · nullable | Yes | |
| impact.quota.kind | string | Yes | |
| impact.quota.would_consume | integer | Yes | |
| impact.quota.limit | integer · nullable | Yes | |
| impact.quota.current | integer | Yes |
Response · 201
Created
data
| Field | Type | Always present | Notes |
|---|---|---|---|
| data | object | object · nullable | Yes |
meta
| Field | Type | Always present | Notes |
|---|---|---|---|
| request_id | string | Yes | |
| dry_run | boolean | Yes | |
| impact | object | No | |
| impact.object | "impact" | Yes | |
| impact.would_create | integer | Yes | |
| impact.would_update | integer | Yes | |
| impact.would_delete | integer | Yes | |
| impact.affected_count | integer | Yes | |
| impact.affected_ids | string[] | Yes | |
| impact.quota | object · nullable | Yes | |
| impact.quota.kind | string | Yes | |
| impact.quota.would_consume | integer | Yes | |
| impact.quota.limit | integer · nullable | Yes | |
| impact.quota.current | integer | Yes |
getSegmentRetrieve a segment
/segments/{segmentId}segments:readExample request
curl "https://api.mailneo.co/api/v2/segments/{segmentId}" \
-H "X-API-Key: $MAILNEO_API_KEY"Authorization
- Required scope
segments:read. A key without it is refused before the endpoint runs.- Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Path parameters1
| Name | Type | Required | Notes |
|---|---|---|---|
| segmentId | string | Yes | max 64 chars |
Response · 200
Success
data
| Field | Type | Always present | Notes |
|---|---|---|---|
| object | "segment" | Yes | |
| id | string | Yes | |
| name | string | Yes | |
| created_at | string | Yes | |
| updated_at | string | Yes |
meta
| Field | Type | Always present | Notes |
|---|---|---|---|
| request_id | string | Yes |
updateSegmentUpdate a segment
Merge-patch at the field level: omit a field to leave it alone. conditions is REPLACED wholesale, never merged — send the complete definition you want, in the same language POST /segments accepts. Neither field is nullable, so null is a 400.
Campaigns that target this segment pick up the new definition on their next send: the audience is evaluated at send time, not saved when the campaign is.
/segments/{segmentId}segments:writeExample request
curl -X PATCH "https://api.mailneo.co/api/v2/segments/{segmentId}" \
-H "X-API-Key: $MAILNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "<string>",
"conditions": {
"operator": "AND",
"conditions": [
"<string>"
]
}
}'Authorization
- Required scope
segments:write. A key without it is refused before the endpoint runs.- Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...- Idempotency
Idempotency-Keyis optional.
Path parameters1
| Name | Type | Required | Notes |
|---|---|---|---|
| segmentId | string | Yes | max 64 chars |
Query parameters1
| Name | Type | Required | Notes |
|---|---|---|---|
| dry_run | boolean | No | 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
| Name | Type | Required | Notes |
|---|---|---|---|
| Idempotency-Key | string | No | 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. · max 255 chars |
Request body
name
| Field | Type | Always present | Notes |
|---|---|---|---|
| name | string | No | max 200 chars |
conditions
| Field | Type | Always present | Notes |
|---|---|---|---|
| operator | "AND" | "OR" | Yes | |
| conditions | (object | object)[] | Yes |
Response · 200
Success
data
| Field | Type | Always present | Notes |
|---|---|---|---|
| data | object | object · nullable | Yes |
meta
| Field | Type | Always present | Notes |
|---|---|---|---|
| request_id | string | Yes | |
| dry_run | boolean | Yes | |
| impact | object | No | |
| impact.object | "impact" | Yes | |
| impact.would_create | integer | Yes | |
| impact.would_update | integer | Yes | |
| impact.would_delete | integer | Yes | |
| impact.affected_count | integer | Yes | |
| impact.affected_ids | string[] | Yes | |
| impact.quota | object · nullable | Yes | |
| impact.quota.kind | string | Yes | |
| impact.quota.would_consume | integer | Yes | |
| impact.quota.limit | integer · nullable | Yes | |
| impact.quota.current | integer | Yes |
deleteSegmentDelete a segment
Permanent, and there is no undo — call with ?dry_run=true first.
A segment is a saved filter: deleting it touches no subscriber, and nothing else in the database references it, so nothing cascades and nothing is detached. The one thing to check first is newsletter campaigns — a campaign that targets this segment keeps its id in the campaign's settings, and a send whose targeting names a segment that no longer exists deliberately delivers to NOBODY (it fails closed rather than widening to the whole list). A scheduled campaign pointed at this segment will therefore go out empty. Retarget those campaigns before deleting.
/segments/{segmentId}segments:writeExample request
curl -X DELETE "https://api.mailneo.co/api/v2/segments/{segmentId}" \
-H "X-API-Key: $MAILNEO_API_KEY"Authorization
- Required scope
segments:write. A key without it is refused before the endpoint runs.- Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Path parameters1
| Name | Type | Required | Notes |
|---|---|---|---|
| segmentId | string | Yes | max 64 chars |
Query parameters1
| Name | Type | Required | Notes |
|---|---|---|---|
| dry_run | boolean | No | 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. |
Response · 200
Success
dataMay be null.
| Field | Type | Always present | Notes |
|---|---|---|---|
| object | "segment" | Yes | |
| id | string | Yes | |
| deleted | true | Yes |
meta
| Field | Type | Always present | Notes |
|---|---|---|---|
| request_id | string | Yes | |
| dry_run | boolean | Yes | |
| impact | object | No | |
| impact.object | "impact" | Yes | |
| impact.would_create | integer | Yes | |
| impact.would_update | integer | Yes | |
| impact.would_delete | integer | Yes | |
| impact.affected_count | integer | Yes | |
| impact.affected_ids | string[] | Yes | |
| impact.quota | object · nullable | Yes | |
| impact.quota.kind | string | Yes | |
| impact.quota.would_consume | integer | Yes | |
| impact.quota.limit | integer · nullable | Yes | |
| impact.quota.current | integer | Yes |
countSegmentCount the subscribers a segment matches
Evaluates the segment against the current subscriber set and returns the number of matches. Every matching subscriber is counted whatever their status; save a segment with a status condition if you want a narrower number.
This is a live aggregate over the subscriber table, not a stored value, so it is slower than an ordinary read. Cache the result rather than polling it.
Segments that filter on newsletter opens, clicks or bounces cannot be evaluated here and return 400 filter_unsupported: those records are not part of the public API, and returning a number computed without them would be silently wrong.
/segments/{segmentId}/countsegments:readExample request
curl "https://api.mailneo.co/api/v2/segments/{segmentId}/count" \
-H "X-API-Key: $MAILNEO_API_KEY"Authorization
- Required scope
segments:read. A key without it is refused before the endpoint runs.- Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Path parameters1
| Name | Type | Required | Notes |
|---|---|---|---|
| segmentId | string | Yes | max 64 chars |
Response · 200
Success
data
| Field | Type | Always present | Notes |
|---|---|---|---|
| object | "segment_count" | Yes | |
| segment_id | string | Yes | |
| count | integer | Yes | |
| counted_at | string | Yes |
meta
| Field | Type | Always present | Notes |
|---|---|---|---|
| request_id | string | Yes |