Segments
Rule-based audiences over project user fields, attributes and tags, with nested and/or groups.
A segment is a saved, named condition tree. Target it with
{ "type": "segment", "segmentId": "seg_…" }. Membership is evaluated each time a user's
feed loads (see read-time evaluation).
Segments need the Growth plan and up (see plan features).
Shape
{
"name": "Pro users in the beta",
"description": "Pro plan and opted into the beta",
"conditions": {
"combinator": "and",
"conditions": [
{ "field": "attributes.plan", "operator": "eq", "value": "pro" },
{ "field": "tags", "operator": "contains", "value": "beta" }
]
}
}A group has a combinator (and or or) and a list of conditions, each either a
condition (field, operator, value) or another group.
Fields
| Field | Type | Notes |
|---|---|---|
tags | string list | Set from your backend only. |
email | string | |
name | string | |
locale | string | The project user's locale, e.g. de-AT. |
timezone | string | As set in the SDK's user profile or from your backend, e.g. Europe/Berlin. The SDKs don't detect it. |
createdAt | epoch ms | When the project user was first created: by their first SDK session, your backend, or a users notification sent to them. |
attributes.<key> | any attribute value | Custom attributes. Keys in conditions may use letters, digits, _, . and - (up to 64 characters). |
Operators
| Operator | Matches when | Missing value (null / not set) |
|---|---|---|
eq | equal | no match |
neq | not equal | matches |
in | equal to one of value (an array) | no match |
not_in | not equal to any of value | matches |
contains | string contains value, or list has an element equal to value | no match |
not_contains | the opposite of contains | matches |
starts_with | string starts with value | no match |
ends_with | string ends with value | no match |
gt, gte, lt, lte | numeric comparison | no match |
exists | the field has a non-null value | no match |
not_exists | the field is missing or null | matches |
Comparison rules:
- String comparisons are case-insensitive (
eq,in,contains,starts_with,ends_with). - Lists: when the field is a list (such as
tagsor a string-array attribute),eqandcontainsmatch if any element matches. - Numbers: if either side is a number, both are compared as numbers, so
"42"equals42. Booleans count as1and0. - Dates:
gt/gte/lt/lteconvert strings to numbers, and strings that aren't numbers are parsed as dates.{ "field": "createdAt", "operator": "gte", "value": "2026-01-01" }compares against midnight UTC on 1 January 2026. - Values can be a string (up to 1,024 characters), number, boolean,
nullor a list of up to 100 strings.inandnot_inneed a list.
Nesting and limits
| Limit | Value |
|---|---|
| Group nesting | 3 levels (the root group plus two levels of nested groups) |
| Conditions per segment | 50, counting groups and conditions at every level |
| Conditions per group | at least 1 |
| Name | 1 to 100 characters |
| Description | up to 500 characters |
{
"combinator": "and",
"conditions": [
{ "field": "attributes.plan", "operator": "in", "value": ["pro", "team"] },
{
"combinator": "or",
"conditions": [
{ "field": "locale", "operator": "starts_with", "value": "de" },
{ "field": "timezone", "operator": "in", "value": ["Europe/Berlin", "Europe/Vienna"] }
]
},
{ "field": "createdAt", "operator": "gte", "value": "2026-01-01" }
]
}Preview before saving
POST /v1/segments/preview evaluates a condition tree over up to 1,000 of the most
recently active project users and returns how many matched, plus up to five examples. It's
an estimate for sanity checks, not an exact count, and it doesn't write anything. It works
on every plan, even without an active subscription.
curl https://api.notificationstream.com/v1/segments/preview \
-H "Authorization: Bearer $NOTIFICATIONSTREAM_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"conditions":{"combinator":"and","conditions":[{"field":"attributes.plan","operator":"eq","value":"pro"}]}}'{ "sampled": 1000, "matched": 132, "examples": [{ "userId": "user_123", "email": "ada@example.com", "name": "Ada Lovelace" }] }Manage segments
| Endpoint | Purpose |
|---|---|
GET /v1/segments | All segments of the project, sorted by name (not paginated) |
POST /v1/segments | Create |
GET /v1/segments/{id} | Read |
PATCH /v1/segments/{id} | Update name, description or conditions. Applies to already published notifications too. |
DELETE /v1/segments/{id} | Delete. Notifications that targeted it stop matching anyone. |
Attributes can come from the client
The SDK may sync profile fields and attributes for its user, so a user can
change their own attributes. Don't use attribute-based segments to gate confidential
content. Use tags (server-only) or users audiences for that. See
Security.