Discover what you can filter on
You cannot guess your own attribute names, so start by reading the catalog instead of writing a filter blind:attr token to use and the operators valid for it:
personAttributes: typed columns on the person (email,status,createdAt) plus every customattributes.<key>your org has ingested.computedVariables: virtualcomputed.<key>values (an aggregate or expression over related data) resolved at filter time, with no stored value of their own.customObjectTypes: each type’s own filterable attributes, addressed asobject.attributes.<key>once you reach that type through a relationship.reachableObjects: the custom-object types reachable from a person, and every relationship path that gets you there, direct or through one or two intermediate objects.operatorReference: every operator’s expectedvalueshape (scalar,list,date,dateRange,relativeWindow,dateOffset,none), so you don’t have to guess whether an operator wants a string, a list, or a window.
Build a filter
A segment’s filter is aFilterExpression: a tree of predicates joined by
AND/OR. Each predicate is one of a few kinds:
A segment for premium customers who have gone quiet on payments. Note the
event predicate: events reach Boom through the API, not through the
database sync, which syncs people, custom objects and
relationships only. If your payment history arrives by database sync, filter on
a custom object’s date attribute with has_relationship instead.
contains and not_contains accept a single substring or a list. A list
means “any of these”: contains matches a row with any needle,
not_contains keeps a row with none of them. To exclude several values,
pass one not_contains with all of them, not a comma-joined string.Author a segment end to end
The catalog, validate, preview, create, evaluate, and members endpoints form one loop. An agent (or a script) can walk it without ever opening the dashboard.1
Read the catalog
GET /api/v1/segments/catalog (segments_catalog). See what’s
filterable before writing anything.2
Validate the filter
POST /api/v1/segments/validate (segments_validate) with a
filterExpression. This is a dry-run compile against your live catalog.
Nothing is saved; a bad attribute or operator fails here with the exact
field to fix.3
Preview the match count
POST /api/v1/segments/preview (segments_preview) runs the same
filter and returns { "count": N }, the number of people it currently
matches. No segment is created.4
Create the segment
POST /api/v1/segments (segments_create) with name, slug, and the
filterExpression. Creating a segment compiles and stores the filter,
it does not evaluate it: the response comes back with memberCount: 0
regardless of how many people actually match.5
Evaluate it
POST /api/v1/segments/{slug}/evaluate (segments_evaluate) runs the
filter now and writes the membership rows, returning
{ "entrants", "leavers", "total" }. Nothing is a member until an
evaluation runs at least once.6
Read the members
GET /api/v1/segments/{slug}/members (segments_members_list) lists
the active members, newest first: name, phoneNumber, externalId,
email, enteredAt. Keyset-paginated with next_cursor, same pattern
as every other list endpoint.PATCH /api/v1/segments/{slug} (segments_update) updates any field except
the slug, which is permanent. Changing filterExpression recompiles the
filter but does not re-evaluate it, so the member set stays exactly as it
was until the next evaluate call.
Keeping a segment fresh
A segment’s membership is a set of rows, not a live query, so it only changes when an evaluation runs. How often that happens isevaluationCadence:
REACTIVE_ONLY(the default). The segment re-evaluates the specific people a CDP write touches, usually within seconds of that write, plus a daily backstop sweep to catch anything a reactive update missed. Nothing else triggers it, so an explicitsegments_evaluatecall is how you force a full refresh.HOURLYorDAILY. A scheduled sweep evaluates the whole segment on that cadence, independent of any particular write. Useful for filters that depend on the passage of time itself, like “hasn’t paid in 30 days,” which can flip with no new data at all.
How a segment reaches outreach
A segment does not attach to an initiative directly. It becomes the trigger on the journey behind that initiative: the journey’s entry node is configured withtriggerType: segment and a segmentId. Once that journey is
published, every evaluation that adds someone to the segment enrolls them,
carrying along whatever data the segment projects for that person.
Wiring that trigger is an app job today. It needs the segment’s internal id,
and this API identifies segments by
slug, so set a segment trigger in the
Boom app rather than over the API.Limits and gotchas
- Create and update never evaluate.
memberCountis0right aftersegments_create, and a filter change fromsegments_updateleaves the existing member set untouched until you callsegments_evaluate. - Weekend freeze. A segment with
skipWeekendEvaluationset rejects an explicit evaluate on Saturday or Sunday (in your organization’s timezone) with400 weekend_evaluation_skipped. - Deleting requires organization admin over MCP.
segments_deleteis a soft delete: it disappears from every list immediately, and its journey triggers are removed so no journey keeps enrolling from it. It is idempotent,{ "removed": false }on a segment that is already gone. - Pagination.
segments_listandsegments_members_listare keyset-paginated, defaulting to 100 rows and capped at 1000; loop onnext_cursoruntil it comes backnull. - Preview returns a count only. It does not return a sample of matching people over the API or MCP, only the total.
- Slugs are permanent. Pick one deliberately;
segments_updatecannot rename it.
Related
Customer Data Platform
The people, objects, and events a segment’s filter reads.
Events
How the events an
event predicate matches against get recorded.Journeys
Set a segment as a journey’s trigger to turn membership into outreach.
Use MCP
Author segments conversationally with the same tools documented here.