Skip to main content

Segment Filters

A segment's filter is a tree of rules. A contact is in the segment when it matches the tree. The same format is returned by Get Segment and accepted by Create Segment and Update Segment, so you can read a filter, change it, and send it back.

Groups​

The root of a filter is always a group. A group combines its conditions with and (all must match) or or (any may match), and a condition is either a rule or another group.

{
"type": "group",
"operator": "and",
"conditions": [
{ "type": "channel_status", "channel": "email", "consent": "subscribed" },
{
"type": "group",
"operator": "or",
"conditions": [
{ "type": "tag", "tag_ids": ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6b"] },
{ "type": "attribute", "field": "properties.plan", "operator": "equals", "value": "pro" }
]
}
]
}

A filter needs at least one rule, and may hold up to 100 rules in groups nested up to 10 levels deep.

Rules​

typeMatches contacts by
attributeA contact field or custom property
list_membershipThe lists they are in
tagThe tags they carry
engagement_countHow many messages they were sent, opened or clicked
last_engagementWhen they last received, opened or clicked a message
message_engagementWhat they did with one campaign or automation
eventThe events they triggered
channel_statusWhether a channel can reach them

attribute​

{ "type": "attribute", "field": "email", "operator": "ends_with", "value": "@example.com" }
fieldOperators
email, first_name, last_name, phone_number, external_idequals, not_equals, contains, not_contains, starts_with, ends_with, exists, does_not_exist
sourceequals, not_equals. value is one of import, manual, transactional, api, event.
created_atequals, not_equals, greater_than, greater_than_or_equal, less_than, less_than_or_equal, date_is_same_month_day. value is a date (YYYY-MM-DD) or an ISO 8601 timestamp.
properties.<key>Any operator above. The key must be one of your organization's properties. greater_than and the other ordering operators need a number or a date.

exists, does_not_exist and date_is_same_month_day take no value. date_is_same_month_day matches when the month and day are today's, which is how a birthday segment is built.

list_membership and tag​

{ "type": "list_membership", "list_ids": ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a"] }
{ "type": "tag", "tag_ids": ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6b"], "negate": true }

Matches contacts in any of the lists, or carrying any of the tags. Set negate to true for contacts in none of them. An ID that does not exist matches nobody.

engagement_count​

{
"type": "engagement_count",
"channel": "email",
"action": "opened",
"operator": "greater_than_or_equal",
"value": 3,
"time_window": { "value": 90, "unit": "days" }
}
FieldValues
channelemail, sms, push
actionemail: sent, delivered, opened, clicked · sms: sent, delivered, clicked · push: sent, opened, clicked
operatorequals, not_equals, greater_than, greater_than_or_equal, less_than, less_than_or_equal
valueA whole number, 0 or more
time_windowOptional. { "value": 1–365, "unit": "days" | "weeks" | "months" }. Without it, the count covers all time.

last_engagement​

{ "type": "last_engagement", "channel": "sms", "action": "clicked", "operator": "greater_than", "value": "2026-01-01" }
FieldValues
channelemail, sms, whatsapp, push, in_app
actionsent, delivered, opened, clicked. SMS has no opened; in-app has only opened and clicked.
operatorequals, not_equals, greater_than, greater_than_or_equal, less_than, less_than_or_equal, exists, does_not_exist
valueA date (YYYY-MM-DD) or an ISO 8601 timestamp. Omit it for exists and does_not_exist.

does_not_exist matches contacts who never did the action, for example everyone who has never opened an email.

message_engagement​

{
"type": "message_engagement",
"target_type": "campaign",
"target_id": "01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6c",
"channel": "email",
"action": "clicked"
}
FieldValues
target_typecampaign, automation, automation_step
target_idThe ID of that campaign, automation or step
channelOptional. email, sms, whatsapp, push, in_app
actionsent, delivered, opened, clicked
negateOptional. true matches contacts who did not do the action.

event​

{
"type": "event",
"event_name": "order_placed",
"count": { "operator": "at_least", "value": 2 },
"time_window": { "value": 30, "unit": "days" },
"where": [
{ "field": "total", "operator": "greater_than", "value": 500, "value_type": "number" }
]
}
FieldValues
event_nameThe name of one of your events
countOptional. { "operator": "at_least" | "at_most" | "exactly", "value": n }. Without it, the rule matches contacts who triggered the event at least once.
time_windowOptional, as in engagement_count
whereOptional. Up to 10 conditions on the event's data, all of which must hold.
negateOptional. true matches contacts the rule would otherwise leave out.

Each where condition names a field declared on the event's schema, an operator, a value and a value_type:

value_typeOperators
stringequals, not_equals, contains, not_contains, starts_with, ends_with, exists, does_not_exist
number, dateequals, not_equals, greater_than, greater_than_or_equal, less_than, less_than_or_equal, exists, does_not_exist
booleanequals, not_equals, exists, does_not_exist

A rule with where conditions reads the stored events, so its time_window cannot reach back further than the event's retention period.

channel_status​

{ "type": "channel_status", "channel": "email", "consent": "subscribed", "deliverability": "deliverable" }
FieldValues
channelemail, sms, whatsapp, push
consentsubscribed, unsubscribed
deliverabilitydeliverable, undeliverable

Set consent, deliverability, or both.

Common segments​

Ready-made filters for audiences most teams need. Each is a complete filter: send it as the filter of Create Segment, replacing the IDs, event names and property keys with your own.

Engaged email subscribers​

Opened at least one email in the last 30 days and still subscribed to email.

{
"type": "group",
"operator": "and",
"conditions": [
{
"type": "engagement_count",
"channel": "email",
"action": "opened",
"operator": "greater_than_or_equal",
"value": 1,
"time_window": { "value": 30, "unit": "days" }
},
{ "type": "channel_status", "channel": "email", "consent": "subscribed" }
]
}

Lapsed customers​

Have ordered before, but not in the last 90 days. The second rule uses negate to match contacts with no order in the window.

{
"type": "group",
"operator": "and",
"conditions": [
{ "type": "event", "event_name": "order_placed" },
{
"type": "event",
"event_name": "order_placed",
"time_window": { "value": 90, "unit": "days" },
"negate": true
}
]
}

High-value customers​

Placed two or more orders over 500 in the last 6 months. where reads the total field declared on the event.

{
"type": "group",
"operator": "and",
"conditions": [
{
"type": "event",
"event_name": "order_placed",
"count": { "operator": "at_least", "value": 2 },
"time_window": { "value": 6, "unit": "months" },
"where": [
{ "field": "total", "operator": "greater_than", "value": 500, "value_type": "number" }
]
}
]
}

Birthday today​

Contacts whose birthday property falls on today's month and day. The segment's members change every day, so it suits a daily campaign.

{
"type": "group",
"operator": "and",
"conditions": [
{ "type": "attribute", "field": "properties.birthday", "operator": "date_is_same_month_day" }
]
}

New contacts created through the API​

Created on or after a date, with the API as their source.

{
"type": "group",
"operator": "and",
"conditions": [
{
"type": "attribute",
"field": "created_at",
"operator": "greater_than_or_equal",
"value": "2026-03-01"
},
{ "type": "attribute", "field": "source", "operator": "equals", "value": "api" }
]
}

Reachable by SMS, never clicked​

Subscribed to SMS with a deliverable number, and no SMS click on record.

{
"type": "group",
"operator": "and",
"conditions": [
{
"type": "channel_status",
"channel": "sms",
"consent": "subscribed",
"deliverability": "deliverable"
},
{
"type": "last_engagement",
"channel": "sms",
"action": "clicked",
"operator": "does_not_exist"
}
]
}

Clicked a campaign, not yet in a list​

Clicked one email campaign and are in none of the given lists: a follow-up audience that leaves out people already handled.

{
"type": "group",
"operator": "and",
"conditions": [
{
"type": "message_engagement",
"target_type": "campaign",
"target_id": "01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6c",
"channel": "email",
"action": "clicked"
},
{
"type": "list_membership",
"list_ids": ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a"],
"negate": true
}
]
}

Either of two groups​

Subscribed to email, and either tagged VIP or on the pro plan. The inner group is an or inside the outer and.

{
"type": "group",
"operator": "and",
"conditions": [
{ "type": "channel_status", "channel": "email", "consent": "subscribed" },
{
"type": "group",
"operator": "or",
"conditions": [
{ "type": "tag", "tag_ids": ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6b"] },
{ "type": "attribute", "field": "properties.plan", "operator": "equals", "value": "pro" }
]
}
]
}

The event examples assume an event named order_placed with a numeric total field, and the property examples assume birthday and plan properties. A filter that names an event field or property your organization has not defined is refused.

Validation errors​

A filter that breaks a rule is refused with 422 validation_error. The message gives the path of each problem inside the tree:

{
"status_code": 422,
"name": "validation_error",
"message": "Invalid filter: conditions.1.operator: \"created_at\" does not accept the operator \"contains\""
}