Features & CustomizationsLoyalty Program

Milestones & Rewards

One-time loyalty milestone rewards: how the claim flow works, what a milestone reward can contain, and the two delivery modes (async webhook or synchronous API) Stash uses to hand the reward to your backend.

A milestone is a one-time reward checkpoint at a specific XP value inside a tier. Every tier has at least one. Unlike tier earn rates, which apply on every purchase, a milestone reward is granted exactly once, when the player reaches the threshold and claims it.

The claim flow

  1. The player's cumulative Loyalty XP crosses a milestone's threshold.
  2. The reward becomes claimable in the loyalty hub.
  3. The player clicks Claim.
  4. Stash delivers the reward to your backend, which grants it.

Claims are player-initiated. Crossing the threshold makes a milestone claimable; it does not auto-grant. Each milestone is claimable once per player.

Milestone reward contents

A milestone reward is a fixed, one-time grant of one or more reward types:

  • In-game currency: the standard case, a fixed amount of a currency your backend grants (for example, 500 coins).
  • Loyalty XP: a milestone can grant additional Loyalty XP.
  • Catalog products (SKU rewards): a milestone can deliver an arbitrary catalog product or bundle instead of currency. See Milestone SKU rewards.

Every reward in the milestone payload is expressed as an itemId (the reward type or catalog SKU) and a quantity. The list comes from your Studio configuration and is authoritative; treat it as server-side truth, never from the client.

Which tier a milestone belongs to

Each milestone lives inside a tier. When a milestone is claimed, the LOYALTY_MILESTONE_CLAIMED webhook reports the owning tier as milestoneTierId, alongside the shared loyalty payload. See Loyalty webhooks.

Reward delivery modes

Stash delivers a claimed reward to your backend through one of two mechanisms, configured per shop with your Stash engineering contact. It is a backend setting, not a Studio toggle.

ModeWhen to useTrade-off
Async webhookSimple grants where eventual consistency is acceptable.No API to implement. Delivery is queued, so there is no immediate error feedback to the player.
Synchronous APIReal-time validation, for example complex eligibility rules.Immediate confirmation, but you must implement an API endpoint Stash calls.

With async webhook delivery, Stash sends LOYALTY_MILESTONE_CLAIMED. With synchronous API delivery, Stash calls your game backend directly and the webhook is not sent.

Make your grant handler idempotent. De-duplicate on userId + milestoneId so a retry or duplicate delivery never grants a milestone reward twice.

Fields

FieldTypeRequiredNotes
NameTextYesDisplay name shown in the loyalty hub.
XP thresholdNumberYesMust fall within the parent tier's XP range. Locked after publish.
IconImage, 162 x 162 pxYesMilestone badge in the loyalty hub.
Background imageImage, 340 x 162 pxYesBanner behind the milestone card.
Reward nameTextYesDisplay name for the reward (for example "Welcome Bonus").
Rewards1+ reward type + amountYesFixed one-time amount per reward type. Locked after publish.

XP thresholds and reward amounts lock when the program is published; display fields stay editable. See Configuration.

How is this guide?