Returns a paginated list of the contacts matching an arbitrary predicate tree — the same engine that
powers segments, but evaluated on the fly without having to create a segment first.
Use GET /contacts when simple keyword/role/segment filtering is enough. Use this endpoint when you need
to combine several conditions with and/or, or to filter on attributes, tags, events or campaigns.
Pagination
This endpoint uses offset-based pagination:
- First request:
POST /contacts/filterwith{ "per_page": 20 } - The response returns a
cursor(the offset of the next page, e.g.20) - Next page: repeat the same body adding
"cursor": 20 - Continue until
cursorisnull(no more contacts)
cursor is an offset, not a contact ID. Keep filters, sort_by and order identical across pages,
otherwise the offset points at a different result set.
Sorting
sort_by:nameoremail. If omitted, contacts are sorted by internal ID (creation order).order:ascendingordescending(default:descending).
Counting before listing
To know how many contacts match a predicate — without paging through them — call
POST /contacts/filter/count with the same filters payload.
The filters field — Predicate Structure
filters field — Predicate Structurefilters is a predicate tree. Omit it (or send null) to match every contact.
Root object:
{
"audience": "<audience_value>",
"operation": { ... }
}audience (required) — restricts the contact type:
"is_any_contact"(or"is_any") — all contacts, users and leads"is_user"— only registered users"is_lead"— only leads"is_not_user"— equivalent to"is_lead", and"is_not_lead"to"is_user": a stored contact is
always either a user or a lead"is_visitor"— always matches nothing here: visitors are not stored contacts, they only exist while
the messenger evaluates a predicate in real time
operation (optional) — a predicate node, either an operation or a condition:
Operation node (combines multiple predicates):
{
"type": "operation",
"operator": "and",
"predicates": [ ... ]
}operator:"and"or"or"(lowercase)predicates: array of condition or operation nodes (they can be nested at any depth)
Condition node (single filter rule):
{
"type": "condition",
"target": "<target>",
"operation": "<operation>",
"value": "<value>"
}Condition Targets and Operations
state — Contact attribute. Requires name (the attribute key, e.g. "plan", or a default contact
field such as "email", "name", "created_at"). The operation set depends on the value type:
- string:
is,is_not,starts_with,ends_with,contains,does_not_contain - number:
greater_than,less_than,equal_to,not_equal - boolean:
is_true,is_false - date:
date_on,date_before,date_after,date_exactly,date_more_than,date_less_than,
date_more_than_hours,date_less_than_hours,date_more_than_ahead,date_less_than_ahead,
date_more_than_hours_ahead,date_less_than_hours_ahead - any type:
has_any_value,is_unknown(novalueneeded)
For date_on, date_before and date_after, value is a date (timestamp or parsable date string). For
the other date operations, value is a number of days (or hours, for the _hours variants).
tag — Contact tag:
operation:is,is_notvalue: tag name (e.g.,"vip")
event — Custom event tracking. Requires name (the event name, e.g. "purchase"):
- count:
count_greater_than,count_less_than,count_is,count_is_not,count_has_any_value,count_is_unknown - first occurrence:
first_on,first_before,first_after,first_exactly,first_more_than,
first_less_than,first_more_than_hours,first_less_than_hours - last occurrence: same as
first_*but with thelast_*prefix value: a number for the count operations, a date or a number of days/hours for the otherswindow(optional, count operations only):{ "operation": "...", "value": ... }to restrict the count
to a time window
segment — Membership in a contacts segment:
operation:is,is_notvalue:segment_id(integer)
company_state — Attribute of a company the contact belongs to. Same name/operation/value
semantics as state.
campaign — Campaign engagement:
name: the campaign ID (integer)operation:sent,not_sent,seen,not_seen,not_sent_or_not_seen,clicked,not_clicked,
replied,not_repliedvalue(optional,clicked/not_clickedonly): the clicked URL to match
campaigns_multiple — Same engagement operations as campaign, but across several campaigns at once:
names: array of campaign IDs (integers)value(optional): the clicked URL to match
Unsupported targets
The predicate language also has targets that cannot be evaluated against stored contacts: the real-time
messenger ones (page, stats, platform, office_hours, incoming_message), the company-scoped ones
(company_type, companies_segment) and every conversation_* target. Using them here returns
422 contact_invalid_filters.
Examples
Example 1 — First page of registered users tagged vip, sorted by name:
{
"per_page": 50,
"sort_by": "name",
"order": "ascending",
"filters": {
"audience": "is_user",
"operation": {
"type": "condition",
"target": "tag",
"operation": "is",
"value": "vip"
}
}
}Example 2 — Contacts on the premium plan who purchased at least once:
{
"filters": {
"audience": "is_any_contact",
"operation": {
"type": "operation",
"operator": "and",
"predicates": [
{ "type": "condition", "target": "state", "name": "plan", "operation": "is", "value": "premium" },
{ "type": "condition", "target": "event", "name": "purchase", "operation": "count_greater_than", "value": 0 }
]
}
}
}Example 3 — Contacts in segment 10 but not in segment 20, second page:
{
"per_page": 20,
"cursor": 20,
"filters": {
"audience": "is_any_contact",
"operation": {
"type": "operation",
"operator": "and",
"predicates": [
{ "type": "condition", "target": "segment", "operation": "is", "value": 10 },
{ "type": "condition", "target": "segment", "operation": "is_not", "value": 20 }
]
}
}
}Example 4 — Contacts created less than 30 days ago who never opened a campaign:
{
"filters": {
"audience": "is_any_contact",
"operation": {
"type": "operation",
"operator": "and",
"predicates": [
{ "type": "condition", "target": "state", "name": "created_at", "operation": "date_less_than", "value": 30 },
{ "type": "condition", "target": "campaign", "name": 1234, "operation": "not_seen" }
]
}
}
}| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
401Authentication credentials are missing or invalid
403You don't have permission to access contacts
422Validation error: malformed or unsupported filters, or invalid pagination/sorting parameters