Dynamic segments
Segments defined by conditions over attributes, balances, tiers, account age and event activity, recomputed on a schedule and usable as any audience.
A segment is a named group of customers. A static segment holds the members you add. A dynamic segment holds every customer who matches a definition: "gold members who have not ordered in 90 days", "customers with more than 500 points", "new accounts in Texas".
Orishare recomputes the members of every dynamic segment about once an hour and stores them.
Campaign audiences, rule conditions over customer.segments, leaderboards and schedules read the
stored members, so a dynamic segment works anywhere a static one does.
Create one
{
"name": "lapsed_gold",
"kind": "dynamic",
"definition": {
"all": [
{ "path": "customer.tiers.status", "op": "eq", "value": "gold" },
{ "path": "customer.activity.order.completed.count_365d", "op": "gte", "value": 1 },
{ "path": "customer.activity.order.completed.days_since_last", "op": "gte", "value": 90 }
]
}
}Send it with POST /v1/segments and an Idempotency-Key. The definition is the same condition
tree rules use: a comparison { path, op, value }, or all, any and not around other
conditions. An invalid definition is refused with 422 rule_invalid, and param names the
offending node.
What a definition can read
| Path | Value | Operators |
|---|---|---|
customer.attributes.<key> | The attribute, any JSON type. Nested keys with dots. | All rule operators |
customer.balances.<currency> | Number: available balance in that currency code | eq neq gt gte lt lte between in not_in exists |
customer.lifetime_earned.<currency> | Number: everything ever earned in that currency | same |
customer.tiers.<program> | String: the level name in that tier program (program id or name) | eq neq in not_in exists |
customer.created_days_ago | Number: whole days since the customer was created | number operators |
customer.activity.<event>.days_since_last | Number: whole days since the last <event>; unknown if none in 365 days | number operators |
customer.activity.<event>.count_30d | Number: <event> events in the last 30 days | number operators |
customer.activity.<event>.count_90d | Number: <event> events in the last 90 days | number operators |
customer.activity.<event>.count_365d | Number: <event> events in the last 365 days | number operators |
customer.activity.<event>.sum_<property>_365d | Number: sum of a numeric event property over the last 365 days | number operators |
<event> is the event name as you send it, dots included: customer.activity.order.completed.count_90d
counts order.completed events. <property> is a top-level numeric property of that event, for
example sum_total_cents_365d for spend. Activity never looks back further than 365 days.
Rules that keep definitions predictable:
- A value that is unknown never matches, and neither does its negation: a currency or tier
program that does not exist, a missing attribute,
days_since_lastfor a customer with no such event. Counts and sums are never unknown; they are0. - A customer without a tier row is at the program's first level.
notmay wrap one comparison of a balance, tier, age or activity path, or any group of attribute conditions. It may not wrap a group that contains those paths, orexistson them. For "never did X", writecount_365d eq 0.- A definition has at most 50 comparisons and reads the activity of at most 8 different events.
- Attribute timestamps are compared as times only when they are full RFC 3339 values with an
offset, such as
2026-03-01T12:00:00Z. - Merged customers count once, under the customer they were merged into, with the balances and activity of both.
Members, freshness and refresh
GET /v1/segments/{id} reports three fields for every segment:
| Field | Meaning |
|---|---|
members | The number of stored members. null for a dynamic segment that was never computed. |
computed_at | When the members of a dynamic segment were last computed. null for static segments. |
stale | true when the stored members do not reflect the current definition yet, or the last computation is over two hours old. Always false for static segments. |
A new or edited dynamic segment is stale until its next computation, which the schedule picks up
within minutes. To compute it right away, call POST /v1/segments/{id}/refresh with an
Idempotency-Key. It recomputes the members and returns the segment.
GET /v1/segments/{id}/members lists the stored members of either kind. Members of a dynamic
segment cannot be added or removed by hand; change the definition instead.
GET /v1/segments/{id}/eligibility/{customer_id} answers for one customer against the current
definition, so it can be ahead of the stored members between two computations.
Use it as an audience
Name the segment wherever an audience is accepted, exactly like a static one:
{ "name": "Win back gold", "status": "active", "audience": { "segments": ["lapsed_gold"] } }A customer starts and stops matching at the next computation, not at the moment their balance or activity changes. Segments defined over attributes only are also checked live at rule time: a customer who starts matching after an attribute change is in the audience at once, and one who stops matching leaves at the next computation.
VIP tiers with yearly windows and benefits
Qualify members per calendar or membership year, keep a tier through the following year, add a grace period, declare benefits and gate offers by tier.
Birthdays and anniversaries
Collect a birthday, an anniversary or any yearly date for a customer and reward it once a year, at a local time in the customer's time zone.