Catalog ManagementReal-time Catalog

Free Items and Streaks

How Stash tracks claims of free items in a Real-time Catalog response, and how streaks tracked by Stash work.

A FreeItem is claimable at no cost. When a player claims one, Stash records the redemption and emits a FREE_ITEM_REDEEMED webhook. Two fields govern how claims are tracked:

  • status: your response is authoritative for whether the gift is offered (AVAILABLE, CLAIMED, or LOCKED), except for items in a streak that Stash tracks for your shop (see Streaks tracked by Stash). After a recorded claim, Stash overrides AVAILABLE to CLAIMED on catalog reads so a reload cannot show a consumed gift as claimable. Outside a Stash-tracked streak it only ever applies that one flip: a CLAIMED or LOCKED status you send is never changed, and Stash never produces AVAILABLE. The override ends at the item's refreshAt; from then on your status alone decides the next period. For items in a Stash-tracked streak, Stash sets AVAILABLE, CLAIMED and LOCKED itself from the player's streak, and ignores the status you send, including when you omit it.
  • claimId (optional): a stable claim identity, separate from itemId. Free items that share a claimId are the same claim for a claim period: claiming any one of them consumes the claim for all of them. When absent, itemId is the claim identity, which is the existing behavior; integrations that do not send it change nothing.

Use claimId when the item you serve varies per player, for example a daily gift whose itemId differs by loyalty tier. Keep the claimId stable across the variants and the claim still de-duplicates as one gift per period, whichever variant a player sees.

Rules to follow:

  • Return at most one item per claimId per response; additional items sharing one may be discarded. A locked preview of a later reward must carry a distinct claimId, or none at all.
  • A repeat claim inside the same window returns an idempotent success: no duplicate FREE_ITEM_REDEEMED webhook, and no second Loyalty XP credit for gifts that grant XP.
  • On the FREE_ITEM_REDEEMED payload, itemId is the item that was granted; claimId is the claim it consumed and appears only when it differs from itemId.

Streaks tracked by Stash

A streak is a run of free items that a player claims one per period, such as a daily gift calendar. For a shop where Stash turns on a streak, Stash tracks each player's progress and decides which item is claimable. A shop has 1 such streak. You supply the rewards.

Send the streak in GetCatalog:

  • One section, every step. Put all the streak's items in one catalog section as free items: one item per step (or one per tier, below), for every sequence, with the rewards for this player. Give each item its own itemId, and do not share a claimId between items: Stash keys the streak's claims itself, so the per-variant claimId pattern above does not apply.
  • Number the steps with streakOrder. Start at 1 and continue across sequences: with 7-step sequences, step 1 of the second sequence is 8. Set it on every free item in the section or on none; when no item sets it, Stash numbers the free items that apply to the player (those without targetTierId and those for the player's current tier) in the order they appear in the section agreed for your shop or, if none is agreed, in the first section of your catalog that contains only free items. Set streakOrder whenever any item in the section sets targetTierId.
  • Keep the layout whole. The numbers run from 1 with no gaps, and they cover a whole number of sequences. The sequence length is set for your shop (7 unless agreed otherwise). Set streakOrder in 1 section only. If the section's items break these rules, Stash hides the streak's free items, and if more than 1 section sets streakOrder, it hides the free items of each of them. When no item sets streakOrder and no section is agreed for your shop, Stash only infers the section, so a section that breaks these rules is shown as you sent it, with your own status values, and Stash tracks no streak in it. If the section agreed for your shop is missing from a response, Stash shows no streak for that response.
  • Vary rewards by loyalty tier if you need to. For a step whose reward depends on the player's tier, send one item per tier with the same streakOrder and that tier's targetTierId, and optionally one item without targetTierId for players with no matching tier. Each number has at most one item per tier and one without targetTierId. Stash shows the player only the item for their current tier, or else the one without targetTierId, and the claim's FREE_ITEM_REDEEMED webhook carries the itemId of the item the player claimed. If you serve only the player's own tier (Stash sends it in the currentTierId query parameter of GetCatalog), send that tier's items.

What Stash does with it:

  • Sets each item's status. Stash shows the player's current sequence only. Earlier steps are CLAIMED, the step that is due is AVAILABLE (CLAIMED once taken), and later steps are LOCKED. Stash also sets maxClaimable: 1 on the step that can be claimed, 0 on every other step. You do not need to send status or maxClaimable on these items; if you do, Stash ignores them, whatever the values.
  • Locks the streak when it cannot decide. If Stash cannot read a player's progress, or, for a streak with tier rewards, their loyalty tier, every step shows as LOCKED and nothing is claimable until it can. With the skip option, the same applies when no step has an item for the player. Players who are not signed in do not see the streak's items.
  • Uses your refreshAt as the period end. Send refreshAt on each item, set to the next reset. Stash uses it as the end of the current period and shows a countdown to it. When it is missing, already past, or more than one period ahead, Stash uses the reset agreed for your shop instead.
  • Resets per shop. A period is a day or a week, starting at a time of day in a time zone agreed for your shop (00:00 UTC unless agreed otherwise). In a time zone with daylight saving, the reset follows local time. Weekly periods start on Monday. Send the same time in refreshAt, and use it for any once-per-period check on your side.
  • Advances only on a claim. A player can claim one step per period, and only the step that is due. Claiming the last step of a sequence moves the player to the next sequence, and claiming the last step of the last sequence wraps the streak to step 1. A missed period sends the player back to step 1 of the sequence they are in, the one holding the step that was due: a player who misses a day in the middle of a sequence starts that sequence again, a player who claimed the last step of a sequence and then misses a day starts the next sequence at its step 1, and after the last step of the last sequence they start again at step 1. A period in which the streak is turned off or its items are missing counts as missed.
  • Handles steps without a reward for the player. When the due step has no item for the player's tier and none without targetTierId, there is nothing to claim. By default the period counts as missed, and as after any miss the player goes back to step 1 of the sequence that step is in. So a player whose tier has no item for a step claims the steps before it, cannot claim that step, and starts the sequence again, every time, never getting past it. To avoid this, send an item for every step to every tier you target, or an item without targetTierId for that step. If step 1 of the sequence also has no item for the player, they stay on it until you send one. If agreed for your shop, the next step that has an item for the player becomes due in the same period instead.
  • Keeps the streak across loyalty seasons. By default a new loyalty season does not change a player's place in the streak. If agreed for your shop, and your loyalty seasons run in Stash, a new season restarts the streak: the player's first claim in a season other than the one of their last claim is step 1, and a claim already made in the current period stays claimed. A time with no live season does not count as a new season. Items for tiers that are not in the live season are not shown.
  • Keeps the player's place when rewards change. If the rewards you send for a player change, for example with their segment, the player stays on the same step and sees the new reward for it. If you send fewer steps than a player has reached, their place wraps back to the matching step from the start.
  • Queues the grant before it records the claim. Grant the reward when you receive the claim's FREE_ITEM_REDEEMED webhook. Stash records a claim only once its webhook is queued, so a streak needs an enabled webhook endpoint for your shop: without one, every claim fails and nothing is recorded. If a claim fails after its webhook was queued, the player's retry in the same period is the same redemption: its webhook carries the same redemptionId as long as the period's refreshAt has not changed, so de-duplicate on it.

How is this guide?

On this page