Skip to main content
Score Streaks are a campaign type that tracks how consistently a customer repeats a specific action over time. Each qualifying event extends the streak and increases the customer’s score. When the score reaches a configured milestone, rewards are automatically granted.

How score streaks work

A score streak campaign tracks three layered ideas: time windows (periods), consecutive activity (streaks), and cumulative score (milestones).

Periods and loss conditions

Every time a customer earns a score point, the campaign opens a period window — a countdown timer during which the customer must send the next qualifying event to keep the streak alive. If the timer runs out before the next event, the cycle is lost: the streak resets to zero and a new cycle begins. The loss window is defined by loss.conditions[0]:
{
  "type": "NO_TRIGGER_EVENT_WITHIN",
  "amount": 1,
  "unit": "DAYS"
}
Supported units: MINUTES, HOURS, DAYS, WEEKS, MONTHS.

Cycles

A cycle is one continuous run of activity without a loss. Each loss or reset starts a fresh cycle identified by a new current_cycle_id. This means multiple cycles can exist in a single streak record over its lifetime, and the history events are stamped with the cycle they belong to.

Milestones

Rules in a score streak campaign define score thresholds that trigger rewards. When a customer’s current_score reaches or exceeds a milestone, that milestone’s effects (rewards) are granted once per milestone per cycle.
Score  1: Milestone reward (first milestone)
Score  5: Higher milestone reward
Score 10: Highest milestone reward
Score 11: Highest milestone reward again (repeats)
If the score crosses multiple milestones at once (e.g. from 0 to 5), only the highest applicable milestone fires.

Scheduled resets

Optionally, a campaign can define a reset calendar that forcefully closes the active cycle on a schedule — daily, weekly, or monthly. This is independent of the loss condition: a customer with a perfect streak will still be reset on the configured date.
{
  "enabled": true,
  "calendar": {
    "frequency": "MONTHLY",
    "day_of_month": 1
  }
}

Score streak lifecycle

StateDescription
ACTIVECustomer has an active streak in progress
CLOSEDCampaign ended; streak is archived
History events stored in the events JSON column:
TypeTrigger
GAINQualifying event processed; score incremented
MILESTONEScore crossed a milestone threshold
LOSSPeriod expired without a qualifying event
RESETScheduled reset fired
MANUAL_RESETSupport agent triggered a manual reset
EVENT_REVERSALA source event was reversed, rolling back the associated score gain

Campaign configuration

FieldTypeDescription
triggerstringEvent type that increments the score (e.g. "PURCHASE")
privacy"PRIVATE" | "PUBLIC"Controls visibility in public-facing APIs
loss.conditions[0].type"NO_TRIGGER_EVENT_WITHIN"Always this value
loss.conditions[0].amountnumberHow many units before the streak is lost
loss.conditions[0].unit"MINUTES" | "HOURS" | "DAYS" | "WEEKS" | "MONTHS"Time unit
reset.enabledbooleanWhether scheduled resets are active
reset.calendar.frequency"DAILY" | "WEEKLY" | "MONTHLY"Reset cadence
reset.calendar.week_starts_on0–6First day of week (0 = Sunday). Required when WEEKLY
reset.calendar.day_of_month1–31Day of month for reset. Required when MONTHLY. Clamped to last day for short months

Milestone rules

Each rule in a score streak campaign must have a milestone score value. When the customer’s score reaches that value within a cycle, the rule’s effects are applied. Multiple rules can define different reward tiers:
{
  "rules": [
    {
      "milestone": 1,
      "conditions": [],
      "effects": [{ "type": "POINTS", "amount": 10 }]
    },
    {
      "milestone": 5,
      "conditions": [],
      "effects": [{ "type": "POINTS", "amount": 50 }]
    },
    {
      "milestone": 10,
      "conditions": [],
      "effects": [{ "type": "POINTS", "amount": 200 }]
    }
  ]
}
  • Conditions in milestone rules are ignored; only milestone and effects matter.
  • When the score reaches milestone 10, only that rule fires — the lower milestones (1, 5) do not fire again for the same score.
  • If a score crosses a milestone for the first time in a cycle, the reward is granted. If the same milestone is crossed again later in the same cycle (e.g. after a reversal), the reward is granted again.

Event reversal behavior

When a source event is reversed:
  1. All GAIN and MILESTONE history events from that source are marked reversed_at.
  2. An EVENT_REVERSAL history event is appended with the negative score delta.
  3. If the reversed event was in the active cycle, current_score, current_streak, period_score, and period_event_count are decremented.
  4. The highest_milestone_score is recalculated from surviving milestone events.
  5. If the score drops below a previously reached milestone, re-triggering the qualifying event will grant the milestone reward again.
Reversal is idempotent: reversing the same event twice has no additional effect.

Manual admin reset

Support operators can force-reset a customer’s active streak cycle without waiting for the loss cron or a scheduled reset. This appends a MANUAL_RESET history event, starts a new current_cycle_id, and zeros score counters for the active cycle. Endpoint (internal): POST /api/admin/score-streaks/reset
FieldDescription
account_idAccount UUID
campaign_idScore streak campaign UUID
customer_idCustomer external id
reasonAudit reason stored in history (for example Support requested reset)
actor_idOptional operator id for the audit trail
Requires the CRON_CHECKSUM header (same secret used by Masivo cron routes). This endpoint is not part of the public Storefront API.

Comparison with punch cards

Score StreaksPunch Cards
TriggerAny event typeAny event type
Progress unitScore (unlimited)Punches (capped)
CompletionMilestones (repeating)Single completion
Time pressureLoss window + optional resetNone
Event historyFull JSON logNone
ReversalScore rollbackStandard wallet
Cron requiredYes (expire lost streaks)No