Skip to content

Segments

A segment is a definition that picks contacts by their tags, custom fields and engagement. Campaigns send to a saved segment (segmentId) or to a definition given inline (segment). Only sendable contacts ever match: subscribed, and, if you’ve turned on sunsetting, not sunset unless the definition sets includeSunset.

A definition has up to three lists: a contact matches when it matches every item in all, at least one in any, and none in none. Each item is a condition or a nested group, and says which it is in type, so client libraries give each kind its own type. Groups can nest up to 3 levels, with 50 conditions in all. An empty definition, {}, matches every sendable contact.

Item Matches contacts that
{ "type": "tag", "tag": "customer" } have the tag
{ "type": "field", "field": "plan", "op": "eq", "value": "pro" } have the field equal to the value (neq for not equal)
{ "type": "field", "field": "plan", "op": "in", "values": ["pro", "team"] } have the field equal to one of up to 100 values
{ "type": "field", "field": "company", "op": "exists" } have the field ("value": false for contacts without it)
{ "type": "field", "field": "orders", "op": "gt", "value": 3 } have a number field above the value (lt for below)
{ "type": "field", "field": "renewal", "op": "before", "value": "2026-12-31" } have a date field before the date (after for after)
{ "type": "score", "min": 40 } have an engagement score of at least 40
{ "type": "opened", "withinDays": 90 } opened an email in the last 90 days ("clicked" for clicks)
{ "type": "created", "withinDays": 30 } were added in the last 30 days
{ "type": "group", "any": [ ... ] } match a nested group, which takes all, any and none too

For example, customers on the pro or team plan who haven’t been tagged churned, and who opened something in the last six months or joined recently:

{
"all": [
{ "type": "tag", "tag": "customer" },
{ "type": "field", "field": "plan", "op": "in", "values": ["pro", "team"] },
{
"type": "group",
"any": [
{ "type": "opened", "withinDays": 180 },
{ "type": "created", "withinDays": 30 }
]
}
],
"none": [{ "type": "tag", "tag": "churned" }]
}

Counting reads every contact in your table, so it gives the exact number a campaign would send to right now.

Terminal window
curl -X POST "$BASE/segments/count" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"definition":{"all":[{"type":"tag","tag":"customer"},{"type":"field","field":"plan","op":"in","values":["pro","team"]}],"none":[{"type":"tag","tag":"churned"}]}}'

The response gives count and evaluatedAt. A definition over the limits, or with a value that doesn’t fit its operator, gives 422 segment_invalid with errors[] pointing at the problem.

Terminal window
curl -X POST "$BASE/segments" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{
"name": "Paying customers",
"definition": { "all": [{ "type": "tag", "tag": "customer" }, { "type": "field", "field": "plan", "op": "in", "values": ["pro", "team"] }] }
}'
curl "$BASE/segments/<sg_...>/count" -H "Authorization: Bearer $KEY"

Check it worked: the segment has an id starting sg_, and its count matches the one above.

Send the version you read in If-Match:

Terminal window
curl -X PATCH "$BASE/segments/<sg_...>" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-H 'If-Match: 1' -d '{"name":"Paying customers (pro and team)"}'

Changing a segment changes who a draft campaign will send to. A campaign that’s already sending built its audience when it started, and doesn’t change.

Terminal window
curl "$BASE/segments" -H "Authorization: Bearer $KEY"
curl -X DELETE "$BASE/segments/<sg_...>" -H "Authorization: Bearer $KEY"

A segment used by a draft, scheduled or sending campaign can’t be deleted: you get 409 segment_in_use.

Check it worked: the delete returns 204, and the segment is gone from the list.

To send to a segment, see Campaigns and stage plans.