Skip to main content
Tiers represent ranked progression on participants and groups. Each tier type (e.g., "loyalty", "status") is an independent track with ordered levels. You might define a loyalty track with Silver, Gold, and Platinum levels, or a status track for new, active, and churned members. Tiers can advance automatically based on counter thresholds, or be set directly by rules. Retention modes control how long a tier lasts, and downgrade policies determine what happens when qualification lapses.

Tier Types and Levels

A tier type defines a progression track. Each type contains ordered levels, where rank determines the hierarchy. Higher rank means a higher tier.

Tier Type Fields

Level Fields

Tier types can be scoped to a single program or defined at the organization level (shared across programs). Program-specific types take precedence over organization-wide types with the same key.

Qualification

Qualification criteria determine when a participant automatically advances to a tier level. Each criterion checks a participant counter against a threshold.
After all participating rule sets finish for an event, Scrip evaluates qualification automatically. If the participant qualifies for a higher-ranked level than their current tier, they advance. Auto-evaluation only upgrades. Downgrades happen through the lifecycle system. If a SET_TIER rule action fires for the same tier type during the same event, auto-evaluation is skipped for that type. This lets rules take explicit control when needed.

Benefits

Each level can carry a benefits object, a freeform JSON payload that Scrip stores and returns whenever you query a participant’s tier state. Use benefits to attach level-specific data that your application acts on: multipliers, feature flags, discount rates, access grants, or anything else tied to the level.
Scrip does not interpret or enforce the benefits payload. When a participant reaches Gold, their tier state response includes "benefits": {"points_multiplier": 2.0, "lounge_access": true, "support_priority": "high"}. Your application reads these values and applies the corresponding behavior. Benefits are also accessible in rule conditions via participant.tiers.<key>.benefits, so you can write rules that check a participant’s current benefits before taking action:

Retention Modes

The retention config in lifecycle controls how long a tier lasts once achieved.

Qualification Periods

For PERIOD_BASED retention, the qualification period defines the evaluation cycle: At the end of each period, Scrip fires a tier_evaluation system event via an internal automation that re-evaluates all tiers and applies the downgrade policy.

Activity Refresh

For ACTIVITY_REFRESH, the duration accepts the Go units s, m, and h plus d (days) and w (weeks), combinable as in "1w2d12h". "365d" and "8760h" are equivalent. Note that m means minutes, not months. The timer restarts on every external event processed for the participant. When the timer expires without new activity, Scrip fires a tier_expiration system event via an internal automation and applies the downgrade policy.

Status Validity

By default, a PERIOD_BASED tier’s status expires exactly at the period boundary. The optional status_validity config extends that grant. A participant who qualifies during a CALENDAR_YEAR period holds status through December 31. With "status_validity": {"extend_months": 1}, their status stays valid through January 31 of the next year, giving them a month of overlap before re-evaluation applies the downgrade policy.

Downgrade Policies

When a tier expires or the qualification period ends, the downgrade policy determines the participant’s new level. Most programs use DROP_TO_QUALIFYING. It re-evaluates the participant’s counters at downgrade time and places them at the level they actually qualify for. The downgrade policy object takes three fields: Set min_level to establish a floor that the participant can never drop below, regardless of qualification.

Grace Periods

Set grace_days on the downgrade policy to defer downgrades. When a downgrade would normally occur, the tier is extended by the grace period instead. If the participant re-qualifies during the grace window, the downgrade is cancelled. If the grace period expires without re-qualification, the downgrade proceeds.

Counter Rollover

The counters config controls what happens to qualifying counters at the end of a qualification period. For example, if the Gold threshold is 2000 and a participant has 2500 at period end, EXCESS rollover sets the counter to 500 for the new period. The qualifying array lists which counter keys are affected by rollover. Counters not in this list are left unchanged.

SET_TIER Rule Action

Rules can assign a tier directly using the SET_TIER action. This is useful for promotions, overrides, or tier logic that goes beyond counter thresholds.
When expires_at is set, Scrip schedules a tier_expiration system event at that time. If the participant qualifies at expiration, they keep the tier. Otherwise, the downgrade policy applies.

Tier State in CEL

Tier state is available in rule conditions via participant.tiers:
Each tier entry exposes: Group tiers are available on each group entry. Prefer groups.exists(g, g.tiers.loyalty.level == "gold") when participants may belong to more than one group.

Tier Transitions

Every tier change is recorded as a transition with the previous level, new level, timestamp, and what triggered the change. Query a tier’s transition history:
The endpoint is scoped to the tier, not to a single participant. It returns level changes across all participants in the program, newest first, with a 24-hour transition count in meta.count_24h. Each record includes the participant_id and external_id, from_level and to_level (absent for no prior level or a downgrade to base), a trigger_kind of RULE, AUTO, SYSTEM, or MANUAL, and the rule_id or event_id that caused the change. For a single participant’s tier changes, use their state history filtered to tier entries:

Viewing Tiers

Inspect tier type definitions and participant tier state through the API.
PATCH merges into the stored tier: fields you omit are left unchanged. To disable lifecycle automation on an existing tier, or remove a level’s qualification, send an empty object ({}) explicitly. Sending null or omitting the field is a no-op. A populated object replaces the whole config rather than deep-merging it, so include every field you want to keep.

Archiving Tiers

Archiving ends a tier’s lifecycle. Use it to retire a progression track you no longer want participants to enter.
Archiving is one-way. The response sets status to ARCHIVED and archived_at to the time of the call, and an archived tier cannot be returned to ACTIVE. A program route only archives a program-scoped tier; it does not archive an organization-level tier that a program inherits. A tier cannot be archived while any ACTIVE or SUSPENDED rule still references its key, in a SET_TIER action or in a CEL condition. The request returns 409 Conflict with code tier_referenced_by_rules, and the error details list the referencing rules so you can archive or update them first. This prevents silently breaking rules: CEL tier accessors default missing keys rather than erroring, so a condition referencing an archived tier would quietly stop matching with no signal. An archived tier stops all new assignment and evaluation:
  • SET_TIER rule actions targeting the tier are skipped. The event still completes rather than failing.
  • Automatic qualification and period-end re-evaluation no longer assign the tier.
  • Manual assignment (PUT) and tier updates (PATCH) are rejected with 409 Conflict and code tier_archived.
Existing participant tier state is preserved as historical. Participants keep the level they hold, and the tier and its levels stay readable through GET requests. A pending tier_expiration for an already-granted assignment still runs to completion, since it finishes the lifecycle of existing state rather than creating a new assignment. Archiving a tier that is already archived returns 409 Conflict with code tier_archived.