Creating Segments
Segments are dynamic groups of users that automatically update as users match or stop matching your criteria. Use them to:
- Target campaigns to specific user groups
- Trigger user journeys
- Compare retention between audiences
- Personalize experiences
- Track conversions by segment
Creating a segment
Step 1: Open the segment editor
- Click Segments in the sidebar.
- Click New Segment (or open an existing segment to edit it).
Step 2: Details
Fill in the Details card:
- Name - descriptive (e.g. "Premium trial users - expiring this week").
- Description - what this segment represents and where it's used.
- Tags - type a tag and press Enter to commit. Tags appear as removable pills below the input. There's no separate Add button.
Step 3: Build the filter groups
The segment editor uses the same v2 filter builder as the campaign and journey wizards. The builder has two blocks:
- Filter Groups - the include block. Users must match these to be in the segment.
- Exclude filters - the exclude block. Users matching any exclude group are removed from the segment, even if they match an include group.
Crawler profiles
Profiles flagged as crawlers or bots are left out of every segment by default, even when they match your filters - the same profiles the Audience page already hides. An Include crawler profiles toggle in the editor turns them back on for one segment when you genuinely want to inspect bot traffic. The size preview, the exact count, the reachable-by-channel breakdown and the audience a campaign or journey actually sends to all honour the same setting, so the number you approve is the number that gets messaged.
v2 chrome at a glance
- Numbered colored badges per group -
1green,2amber,3blue,4pink,5purple - replacing the old "Filter Group N" header. Colors rotate by group index so it's easy to track which group you're editing. - AND/OR ConnectorChip between sibling rows AND between groups. The chip is the toggle - there's no separate "Match ALL/ANY" header per group, no top-level "Match ALL groups" radio.
+ Add condition- dashed button at the foot of each group.+ Add filter group- dashed button below the last group in the block.- Confirm modal before deleting a non-empty group ("Remove this filter group? It contains N condition(s)…"). Empty groups delete silently.
- Live count pills per group - re-evaluate ~400ms after edits stop, so you always know how many users each group matches.
- Event and attribute pickers are searchable comboboxes. When you type a name that doesn't match anything, the dropdown surfaces an extra
+ Create event "<query>"(or+ Create attribute "<query>") row that registers the schema entry on the spot - useful when you're building segments before tracking is in place.
Filter rows render as bordered cards so each row is a distinct unit. There's no drag handle - reordering filters inside a group has no effect on evaluation (AND / OR are commutative).
Step 4: Save
Click Save. The segment becomes available immediately for campaigns, journeys, and dashboards. Membership refreshes in the background as user attributes and events change.
Filter types
The filter-type dropdown offers exactly these, and the same set in the segment editor, the campaign Audience step and the journey Audience step:
User Attribute · Default User Attribute · Event Performed · Behavioral · In Segment · Custom Entity · App Usage · Canvas Journey · Channel Subscription · List Membership · Bounce Status · E-Commerce · Wallet pass · Warehouse synced list · Device · Random bucket number · Came from import · Message activity
Each is described below.
User attribute
Match on a custom attribute you've defined for your own contacts.
plan equals "premium"
mrr greater than 1000
signup_date within last 30 days
Default user attribute
Match on one of the built-in profile fields every contact has: email, phone, firstName, lastName, country, language, timezone, createdAt, isBot, the device / OS fields, and the ML scores (ml_engagement_score, ml_churn_score, ml_churn_risk, ml_channel_preference, predictive_event_score, predictive_event_likelihood). Same operators as a custom attribute; the difference is that the field comes from a fixed list rather than your own schema, so the picker knows its type and offers the right value input - a country picker rather than a free-text box, Yes/No for a boolean, a low / medium / high / critical select for churn risk.
Contacts without a value
A contact who has no value for a field (it was never set, or it is empty) is treated the same way everywhere - in the segment count, in journeys and at send time:
- Negative conditions match them: is not, does not contain, is not one of and does not exist. "Plan is not Free" includes contacts with no plan.
- Positive conditions do not: is, contains, is one of, exists, and every comparison - including less than and at most. An unknown value is not a small one, so "Score at most 5" leaves out contacts with no score. Combine it with does not exist in an OR group to include them.
- Yes/No fields (such as
isBot) read an empty value as No. - ML scores: a contact the model has not scored yet has no value, so the same rule applies - "Churn risk is not high" includes them.
- Counts are different: an event count or a message count starts at zero, so "performed at most 3 times" includes people who never performed the event, and "at least 0 times" is everyone. The number of times must be a whole number, 0 or more.
- Dates: a date field stored as text (such as
lastEventTime) is compared as a date - on or before a day includes that whole day.
Event
Match on whether (or how often) a user performed a tracked event, optionally filtered by event properties and time range.
Performed "purchase" within last 30 days
Performed "feature_used" at least 5 times
Did NOT perform "onboarding_completed"
Performed "purchase" with property amount >= 100
People who unsubscribed from a campaign or journey
Pick the event message.unsubscribed, add the property campaignId (or canvasId for a journey) and choose the campaign or journey by name, then set the time range - for example in the last 30 days. Leave the property out for everyone who unsubscribed from anything in that window. The same name picker works on every message event: message.opened, message.clicked, message.delivered and the rest.
The same values, N times
On the count operators (at least / at most N times) you can add with the same values of - one to five event properties. The count is then taken per distinct combination of those values, and the person matches when any combination reaches N.
Performed "hotel_viewed" at least 2 times in the last 2 days with the same hotel and check_in
That is "people who viewed any one hotel twice for the same dates" - not two different hotels, not the same hotel for different dates. The live count and the membership use the same grouped query, so the number you see is the number you target. To start a journey on the second such view, use the Event trigger's repeat check instead - a segment cannot know which view was the second one.
Behavioral
Activity-based shortcuts: active_last_7_days, active_last_30_days, inactive_7_days, inactive_30_days. Only the contact's own events count as activity - messages sent to them, opens and journey steps do not.
Segment
Membership in another saved segment. Operators: in_segment / not_in_segment. Useful for nested segmentation:
In segment "VIP Customers"
NOT in segment "Churned Users"
Entity
Match on the user's relationship to custom entity records (orders, subscriptions, etc.).
App
Whether the user has used a specific app (or any app on a platform) in the last N days.
Canvas execution
Users currently in / who have completed / who have exited a specific journey (or any journey).
Reached a step. After you choose a journey, you can pick one of its steps - any kind: a message, a delay, a branch, a split. The filter then matches people whose run reached that step, combined with the version and status you pick (Is not in canvas with a step = never reached it). Every step a run enters is recorded, so this covers long journeys too: a run that loops through hundreds of steps still matches the first step it reached.
When they last reached it. With a step chosen, a time option narrows it to people who last reached the step in the last N days, or between two dates (Last reached: any time is the default). It counts the last time the person entered the step, so someone who reached it in March and again yesterday counts as yesterday. A journey filter saved without a journey matches nobody.
In any journey with tag. Is in any journey with tag and Is not in any journey with tag take a tag instead of a journey and match runs of every journey that carries the tag now, with the status you pick. The filter keeps the tag itself, not its name, so renaming the tag on the Tags page does not change who matches.
Channel subscription
Subscribed / not-subscribed to a specific channel (email / SMS / WhatsApp / push) with optional status filter.
List membership
Is / isn't a member of a subscription list.
Bounce status
Email validity status: email_valid, email_bounced, email_hard_bounced, email_soft_bounced.
Wallet pass
Whether the user currently holds an active wallet pass, optionally scoped to a specific pass template and/or wallet platform (Apple / Google). Operators: has_active / not_has_active.
E-Commerce
RFM segment match, order / spend / cart filters, category and brand affinity, and the predictive purchase-cycle scores - Predicted CLV (next 365 days), Predicted CLV tier, Predicted Next Order (days), Purchase Overdue. Useful for "all Champions" or "users with total spend ≥ $500".
Order, spend and cart filters
These filters read figures kept on each contact and recomputed from the contact's orders every time an order arrives or changes - from your store (Shopify, WooCommerce, Magento, PrestaShop), the Orders API, the SDK or a demo workspace. Cancelled and fully refunded orders do not count; a partial refund is taken off the total spent. Money is in the workspace reporting currency.
| Filter | A contact with no orders (or no cart) |
|---|---|
| Total orders, Historical CLV (total spent), Cart value | Counts as 0. "Total orders ≥ 0" is everyone; "Total orders ≤ 2" includes people who never bought; "Total orders ≥ 1" is the same people as Has made a purchase. |
| Avg order value, Days since last order, Avg days between purchases | Never matches, in either direction - there is no average, last order or rhythm to compare. "Days since last order ≥ 30" means bought, but not in the last 30 days; add Never purchased in an OR group to include people who never bought. |
| Historical CLV tier, Buys category / brand, Top category / brand | Never matches - these exist only for buyers. |
Days since last order is measured against the current time whenever the filter runs, so it is always up to date. Avg days between purchases needs at least three orders. Has an open cart, Cart value and Has abandoned cart include carts from your connected store as well as carts built with the SDK. Has an open cart is a cart with items that was not ordered - abandoned carts included; for abandoned carts only, use Has abandoned cart. Cart value is the cart's items subtotal, before shipping and tax - the value E-Commerce → Carts shows.
Journeys, in-app messages and campaign audiences evaluate every one of these filters the same way.
Coupon filters
Three E-Commerce filters answer "who used a coupon", each optionally limited to in the last N days:
| Filter | Matches |
|---|---|
| Used coupon code | People who used this code on an order. Any letter case matches (summer10 finds SUMMER10). |
| Used any coupon | People who used at least one coupon code on an order. |
| Never used a coupon | People who have not used a coupon code - including people who never bought. With a window: no coupon in the last N days. |
Cancelled and fully refunded orders do not count as using a code. The filters read the codes captured from your store or the Orders API (see Orders - Coupons); journeys and campaign audiences evaluate them the same way.
Store and channel filters
Two E-Commerce filters answer "where did they buy":
| Filter | Matches |
|---|---|
| Bought in sales channel | People with an order in this channel - online, point of sale, app, phone and manual, marketplace or other. |
| Bought in store | People with an order in this store - a connected store, or a storeId your API orders send. |
Each can be limited to in the last N days and to at least N orders (1 to 20) - for example "at least 2 orders at the point of sale in the last 90 days". Cancelled and fully refunded orders do not count. Journeys and campaign audiences evaluate them the same way. See Store and sales channel.
Warehouse synced list
Is / isn't in a list imported from your data warehouse. Operators: in_sync / not_in_sync, then pick the synced list. The row shows where the membership came from and when it last refreshed, and says so plainly if the list has never run or its last run failed - a list that quietly lags its warehouse query is worse than one that obviously does.
Device
Match on the devices a contact has registered: whether a push could reach them at all (Can receive a push / Cannot receive a push / Has a known device), or a device field the SDKs report - equals, not-equals, contains, plus App version is at least / App version is below for version comparisons.
Random bucket number
Every contact is given a random number from 0 to 9999 when their profile is created. It is worked out from the profile's ID, so it never changes for that person - not when they log in, update their details or come back a year later - and the numbers are spread evenly, so any range of them is a fixed slice of your audience: 1,000 buckets is 10%, 5,000 is half.
That makes it the tool for groups that must stay the same across sends:
- A global holdout. Add
Random bucket number is between 0 and 999as an exclude group on every campaign and journey audience. The same 10% never receives marketing, so you can compare them with everyone else to measure what your messaging actually adds. - A stable A/B split. Target
is less than 5000with one version andis at least 5000with the other. Each person sees the same version every time, in every channel. - A test that stays clear of the holdout.
between 1000 and 5499andbetween 5500 and 9999split the remaining 90% in half without touching buckets 0-999.
Operators: is, is not, is less than, is at most, is greater than, is at least, and is between, which takes two numbers and includes both ends. Values must be whole numbers from 0 to 9999; anything else is rejected when you save.
Only a contact who has a bucket number matches - with every operator, is not included. A brand-new profile can be without one for a moment after it is created, so it is in neither side of a split until it gets its number. Segments, campaign audiences and journey conditions all read the same number, so a person in the holdout is in it everywhere.
For a split that should be random for each send rather than fixed per person, use campaign variants instead.
Came from import
Contacts that a contact import - a CSV or JSON file on Data → Bulk Import - created or updated. Pick one or more past imports (each is listed by file name, date and number of rows) and choose:
- is any of - the contact was created or updated by at least one of the chosen imports.
- is none of - none of the chosen imports touched the contact. This includes contacts that were never imported at all.
The quickest way in is the Create segment button on a finished import - see Bulk Import. Segments, campaign audiences and journey conditions all read the same record, so a contact is "from" an import everywhere or nowhere.
A contact keeps its 50 most recent imports; older ones drop off first. Only contact imports count - event imports, entity imports and warehouse syncs do not mark contacts (use Warehouse synced list for a sync).
Imports that ran before this filter was released did not record their contacts, and that cannot be reconstructed afterwards. Those contacts never match is any of and always match is none of. Importing the file again marks them.
Message activity
Match people by what happened to the messages you sent them, built as one sentence:
[Received / Was delivered / Opened / Clicked / Bounced / Unsubscribed / Failed] [from a campaign / from campaigns with tag / from any campaign / from a journey / from a journey step / from journeys with tag / from any message] [on any channel / by email / by SMS / by push / on WhatsApp / in-app / by webhook / on Viber] [at any time / in the last N days / between dates / at least N times / at most N times / but not in the last N days]
- Received means the message was sent - handed to the email, SMS, push, WhatsApp, Viber or webhook provider (the
message.sentevent). Was delivered is the provider's or the device's delivery receipt. - The channel list offers only channels that report the action: there is no "opened" for SMS, push or webhook, and "bounced" is email only. Opened on WhatsApp is a read receipt; opened in-app is the message being displayed.
- Opened by email (or on any channel) also matches people who clicked the email: a click proves the email was opened, even when images were off. Bot opens and link-scanner clicks never count. At least N times and at most N times compare the larger of the two counts, opens or email clicks.
- In-app messages count only when you choose the in-app channel; "on any channel" covers the other channels.
- Campaigns / journeys with tag use the tags the campaigns or journeys carry now - tag a campaign later and its earlier sends count too. Renaming the tag does not change who matches.
- But not in the last N days matches people who did it before but not lately; someone who never did it is not included. For "never", use an Event filter with Did not perform.
- It reads the same events as the Event filter, so it counts exactly what the equivalent Event filter counts, and the live count, the saved segment and a journey condition all give the same answer. The one difference is Opened by email or on any channel, which also reads email clicks (see above) - the equivalent there is an Event filter on
message.openedOR one onmessage.clickedby email.
| You want | Build |
|---|---|
| Received campaign X | Received - from a campaign: X - on any channel - at any time |
| Received any campaign in the last 7 days | Received - from any campaign - on any channel - in the last 7 days |
| Opened an email in the last 30 days | Opened - from any message - by email - in the last 30 days |
| Hasn't opened an email for 30 days | Opened - from any message - by email - but not in the last 30 days |
| Clicked a WhatsApp message | Clicked - from any message - on WhatsApp - at any time |
| Got the message from a journey step | Received - from a journey step: journey and step - at any time |
| Was in a journey step | The Canvas Journey filter: Is in canvas - journey - step |
| In any journey with tag | The Canvas Journey filter: Is in any journey with tag - tag |
Combining filters
AND / OR within a group
Use the ConnectorChip between rows to switch a group's logic between AND and OR. AND means every condition must match; OR means at least one must match.
Group 1 (AND between rows):
plan = "trial"
AND signup_date within last 14 days
AND email_verified = true
Matches: trial users who signed up recently AND have a verified email.
AND / OR between groups
Use the ConnectorChip between groups to switch how groups compose. AND means a user must match every group; OR means matching one group is enough.
Group 1 (AND): plan = "trial" AND signup_date within last 14 days
OR
Group 2 (AND): performed "feature_used" >= 3 times AND performed "purchase_completed"
Matches: recent trial users OR users who are active and converted.
Exclude filters
The Exclude block uses identical chrome - numbered badges, ConnectorChip, dashed + Add buttons, confirm-on-delete, live count pills. Users matching ANY exclude group are removed from the segment, even if they're in an include group.
Live count
The right rail tracks total segment size as you edit:
- Total matching count + percentage of the workspace
- Reachable by channel - of those matching users, how many a campaign on each channel could actually reach: they have an address or an active device, they have not unsubscribed, and their address or token has not hard-bounced. It is the send path's own test, not just a subscription flag. The phone STOP ledger and the address suppression lists are re-checked at send time and are not applied here, so a real send can only be smaller than the number shown
- Re-evaluates with the same ~400ms debounce as the per-group pills
- Updates when you switch the AND/OR ConnectorChips
Approximate by default, exact on demand
The live size is an approximate estimate by default, shown with a leading
≈ (for example ≈ 12,400). This keeps the editor responsive even on
workspaces with millions of users. When you want the precise figure, click
Calculate exact - the ≈ drops and the count is recomputed exactly.
A segment that has an exclude group skips the estimate and is counted exactly
from the start, so it arrives with no ≈ and no Calculate exact button.
"Include AND NOT exclude" has no cheap honest approximation: the old shortcut
subtracted a workspace-wide exclude count from the include count, which floored
almost every such segment at zero.
This estimate is only ever a preview of size. The segment's actual membership - and the audience a campaign or journey sends to - is always computed exactly. You never send to an estimate.
Growth analytics
For a saved segment, the editor shows a Growth card that tracks how the segment's size changes over time:
- Current size and a trend indicator - growing / declining / stable.
- Average daily growth and the segment's peak size.
- A chart of size over time.
A daily job records a size snapshot per segment. If you've just saved a segment and there's no history yet, click Snapshot now to seed the first data point immediately.
Segment examples
Trial conversion opportunity
Trial users approaching expiration who haven't converted:
- Group 1 (AND):
plan = "trial"ANDtrial_ends_date within next 3 daysANDdid NOT perform "purchase_completed"
Power users
Highly engaged users:
- Group 1 (AND):
performed "login" >= 20 times within last 30 daysANDperformed "feature_used" >= 50 times within last 30 days
At-risk paying customers
Paying users showing churn signals:
- Group 1 (AND):
plan = "paid"ANDinactive_30_daysANDlifetime_value > 500
Onboarding drop-off
Started but didn't complete:
- Group 1 (AND):
performed "signup_completed"ANDdid NOT perform "onboarding_completed"ANDsignup_date within last 7 days
Cross-platform users
Active on multiple platforms:
- Group 1 (AND): used app on
webAND used app onios
Best practices
Keep segments focused
"Premium users in US who haven't logged in for 7 days" "All users with various random criteria"
Use descriptive names
"Trial users - expiring this week" "Segment 1"
Document purpose
Use the description to explain what the segment represents, which campaigns/journeys use it, and when to revisit the criteria.
Start simple, iterate
Phase 1: plan = "premium"
Phase 2: plan = "premium" AND active_last_30_days
Phase 3: plan = "premium" AND active_last_30_days AND performed "feature_x" >= 5 times
Dynamic membership
Segments update in real time as user attributes and events change. There's no manual refresh - when a user signs up, upgrades, or churns, segment membership reflects it within seconds.
Keeping a segment ready for reports
Analytics reports that filter by a segment work out who is in it first, then analyse those people. For a very large segment that is expensive enough that the report measures a random sample instead - retention comparison does this above 50,000 members, and says so on the chart.
Turning on Keep this segment ready for reports, on the segment itself, stores its membership ready to use. Reports then analyse the whole segment, however large it is.
Two things to know before you switch it on:
- It is a nightly snapshot. Membership is rebuilt every night, and whenever you press Rebuild now. Reports using it show the time it was built, so you can always see how current the answer is. Everything else about the segment - campaigns, journeys, the live count - is unaffected and still updates in real time.
- Each organization has a limited number of slots, twenty by default, shared across all of its workspaces. If they are all in use, the switch tells you which segments are using them so you can free one. Ask us if you need more.
Turn it on for the handful of segments you compare in reports regularly. There is no benefit to turning it on for a segment you only use to send campaigns.
Limits
- Filters per segment: unlimited
- Filter groups: unlimited
- Segments per workspace: unlimited
- Segments kept ready for reports: 20 per organization by default