Skip to main content
Score Streaks reward customers who consistently send a qualifying event within a defined time window. This guide walks you through creating a campaign, reading streak state from the API, and handling rewards in your frontend.

Prerequisites

  • An active Masivo account with a SERVER API key
  • A customer registered in Masivo with an external_id matching your system’s customer identifier
  • At least one reward configured in your account

Create the campaign

1

Open the Score Streaks section

In the dashboard navigate to Campaigns → Score Streaks and click New campaign.
2

Set the trigger event

Choose the event type your customers will send (e.g. PURCHASE, CHECK_IN). Every time a customer sends this event it counts toward their streak.
3

Configure the loss condition

Set how long a customer has to send the next qualifying event before the streak is broken. For example, 1 DAY means the customer must check in daily to keep their streak alive.
4

Add milestone rewards

Click Add milestone to define score thresholds. Each milestone triggers a reward when the customer’s score reaches that number in the current cycle.Example milestone tiers:
  • Score 1 → 10 points (daily reward)
  • Score 7 → 100 points (weekly bonus)
  • Score 30 → 500 points (monthly champion)
5

Optional: enable scheduled resets

Toggle Scheduled reset if you want streaks to restart on a fixed calendar date regardless of the customer’s activity — useful for monthly leaderboards or seasonal campaigns.
6

Publish the campaign

Set start date and publish. The campaign will start processing events immediately.

Send qualifying events

Use the Emit event endpoint from your backend:
POST /api/storefront/v1/behavior/events
Authorization: Bearer <SERVER_API_KEY>
x-account-id: <ACCOUNT_ID>

{
  "customer_id": "user_abc123",
  "brand_id": null,
  "type": "PURCHASE",
  "issued_at": "2026-05-21T12:00:00Z"
}
Masivo will:
  1. Identify matching score streak campaigns for this event type
  2. Find or create the customer’s score_streak record
  3. Check if the loss window expired (and close the cycle if so)
  4. Increment the score and write a GAIN history event
  5. Fire any milestone rewards and write MILESTONE history events
  6. Return the API response — rewards accumulate in the customer’s wallet

Display streak progress

Fetch the customer’s active streaks to show a live progress indicator:
GET /api/storefront/v1/customers/{customer_id}/score-streaks
Authorization: Bearer <SERVER_API_KEY>
x-account-id: <ACCOUNT_ID>
Example response:
{
  "data": [
    {
      "id": "b3e9a1f2-...",
      "campaign_id": "c8d2...",
      "customer_id": "user_abc123",
      "status": "ACTIVE",
      "current_score": 5,
      "current_streak": 5,
      "highest_milestone_score": 5,
      "period_started_at": "2026-05-21T12:00:00Z",
      "period_ends_at": "2026-05-22T12:00:00Z",
      "period_score": 5,
      "period_event_count": 5,
      "next_reset_at": null,
      "last_activity_at": "2026-05-21T12:00:00Z",
      "last_loss_at": null,
      "last_reset_at": null
    }
  ]
}
Key fields for your UI:
FieldUse
current_scoreCurrent streak score in this cycle
period_ends_atDeadline for next qualifying event (show a countdown)
current_streakTotal qualifying events in current cycle
last_loss_atLast time the streak was broken

Show streak history

Pass include=history to get a flattened, time-ordered list of all history events across all streaks for this customer:
GET /api/storefront/v1/customers/{customer_id}/score-streaks?include=history&limit=50
Response shape when include=history:
{
  "data": {
    "streaks": [ ... ],
    "history": [
      {
        "id": "evt1...",
        "score_streak_id": "b3e9...",
        "campaign_id": "c8d2...",
        "customer_id": "user_abc123",
        "cycle_id": "cycl1...",
        "type": "MILESTONE",
        "score_delta": null,
        "score_before": 4,
        "score_after": 5,
        "milestone_score": 5,
        "period_started_at": "2026-05-21T12:00:00Z",
        "period_ends_at": "2026-05-22T12:00:00Z",
        "reversed_at": null,
        "reason": null,
        "actor_id": null,
        "metadata": {},
        "created_at": "2026-05-21T12:00:00Z"
      }
    ]
  }
}
Use history to render an activity feed, a streak timeline, or to debug unexpected state.

Common patterns

Show a countdown timer

const streak = streaks[0];
const deadline = new Date(streak.period_ends_at);
const msLeft = deadline.getTime() - Date.now();
const hoursLeft = Math.floor(msLeft / 3_600_000);

Detect a broken streak

const wasBroken = streak.last_loss_at !== null;
const brokenAt = wasBroken ? new Date(streak.last_loss_at) : null;

Find the next milestone

Given the campaign rules sorted by milestone ascending:
const nextMilestone = rules
  .filter(r => r.milestone > streak.current_score)
  .sort((a, b) => a.milestone - b.milestone)[0];
const pointsNeeded = nextMilestone.milestone - streak.current_score;

Frequently asked questions

The associated GAIN and MILESTONE history events are marked as reversed and an EVENT_REVERSAL history event is appended. If the reversed event was in the active cycle, the score and streak count are rolled back. The operation is idempotent.
The cron job (/api/cron/loyalty/score_streaks) runs periodically. When it finds a streak whose period_ends_at is in the past, it closes the active cycle with a LOSS history event. The next qualifying event from the customer starts a fresh cycle.
Yes — once per cycle. If the streak is lost (new cycle) and the customer reaches the same milestone again, the reward is granted again. Within a single cycle, each milestone fires exactly once.
The loss condition is triggered by inactivity: the customer fails to act before the period expires. A scheduled reset fires on a fixed calendar schedule regardless of activity — even a customer with a perfect streak is reset on the configured date.
Use the internal admin endpoint POST /api/admin/score-streaks/reset with the account_id, campaign_id, customer_id, and a reason. This requires the CRON_CHECKSUM header and is intended for support operations.