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
- The player's cumulative Loyalty XP crosses a milestone's threshold.
- The reward becomes claimable in the loyalty hub.
- The player clicks Claim.
- 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.
| Mode | When to use | Trade-off |
|---|---|---|
| Async webhook | Simple grants where eventual consistency is acceptable. | No API to implement. Delivery is queued, so there is no immediate error feedback to the player. |
| Synchronous API | Real-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
| Field | Type | Required | Notes |
|---|---|---|---|
| Name | Text | Yes | Display name shown in the loyalty hub. |
| XP threshold | Number | Yes | Must fall within the parent tier's XP range. Locked after publish. |
| Icon | Image, 162 x 162 px | Yes | Milestone badge in the loyalty hub. |
| Background image | Image, 340 x 162 px | Yes | Banner behind the milestone card. |
| Reward name | Text | Yes | Display name for the reward (for example "Welcome Bonus"). |
| Rewards | 1+ reward type + amount | Yes | Fixed 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?
Tiers
How loyalty tiers work: XP thresholds, the stable string tier ID your backend receives on webhooks, per-tier earn rates, and how tier changes are reported on purchase and refund.
Milestone SKU Rewards
Configure a catalog product (SKU) as a milestone reward in Stash Studio: the tabbed reward picker, adding products and reward types inline, the milestone preview, and how SKU rewards appear on the claim webhook.