Skip to content

Upgrader

The Upgrader is a single-shot trade-up game: the player stakes value, picks a win chance, and picks a target prize from the candidates the server allows for that stake. One provably-fair roll decides it — win grants the target item to the player’s inventory, loss grants nothing. Either way the stake is spent. The stake can be a balance amount or owned inventory items (see Wager-items mode).

These endpoints back a backend-driven Upgrader (your own frontend, or the storefront’s native render). To embed the Upgrader as an iframe instead, mint a launch with gameKey: "upgrader" — see Game-target launch below.

The Upgrader must be in your operator’s enabled games, and its win chances, house edge, and wager limits are configured in your operator admin under Settings → Upgrader. Prizes are drawn from your live catalogue — any active, fulfillable item whose value lands in the computed band is a candidate.

List candidate prizes

GET /api/s2s/upgrader/prizes resolves the target value band for a chosen wager + chance and returns the in-band catalogue items (highest value first), so your frontend can render the prize carousel and let the player pick a target. Read-only — nothing is staked or drawn here.

GET https://{operator}.app.lootboxsolutions.com/api/s2s/upgrader/prizes
?playerExternalId=u_8431&wagerMinor=10000&chanceBps=2500&currency=EUR
FieldRequiredNotes
playerExternalId✅The player the prizes are listed for.
wagerMinor✅The stake, in minor units of the display currency.
chanceBps✅The chosen win chance in basis points (2500 = 25%). Must be one of the offered presets (see config.chancePresetsBps).
currency—ISO-4217 display currency to price the band + prizes in. Defaults to the operator currency; must be one of your configured currencies.
locale—BCP-47-ish content locale. Any value is served, falling back to best-available copy.

Response — 200

{
"wagerMinor": 10000,
"chanceBps": 2500,
"feeBps": 500,
"currency": "EUR",
"targetBand": { "lowerMinor": 34545, "upperMinor": 38000 },
"prizes": [
{ "itemId": 906, "name": "…", "valueMinor": 38000, "currency": "EUR", "imageUrl": "https://…" }
],
"config": {
"houseEdgeBps": 500,
"chancePresetsBps": [1000, 2500, 5000, 7500, 9000],
"defaultChanceBps": 2500,
"maxChanceBps": 9500,
"available": true,
"minWagerMinor": 100,
"maxWagerMinor": 1000000,
"currency": "EUR",
"modes": { "balance": true, "items": false }
}
}
FieldNotes
targetBand.lowerMinor / upperMinorThe value window a target prize must fall in. Higher chance → narrower, lower band; lower chance → richer prizes. With no-loss on, lowerMinor is never below the stake.
prizes[]In-band candidates, highest value first. Default the player’s selection to prizes[0] (closest to the band top → best value). imageUrl may be null.
prizes[].valueMinorThe item’s value in the requested currency, repriced from the operator base.
config.chancePresetsBpsThe win-chance options to offer: the operator’s presets at or under maxChanceBps. defaultChanceBps is pre-selected and always one of them.
config.maxChanceBpsThe largest chance a player may buy — you can’t buy a sure thing, so it’s capped below 100% by the house edge, and lower still with no-loss on. 0 when no chance can be offered.
config.availablefalse when no chance can be offered (chancePresetsBps is empty): hide or disable the Upgrader. Every prizes and play call is then refused.
config.minWagerMinor / maxWagerMinorThe wager limits, in the requested currency. The max bounds the richest winnable prize.
  • Amounts are in the requested currency — the band, every prize value, and the wager limits, all repriced from the operator base at the current rate.
  • The list is bounded; a wager with no in-band catalogue item returns an empty prizes array (the band still resolves).

With no-loss on

When the operator sets a Nuggets cost in Settings → No-loss (see noLoss.nuggetsCostPercent), a lost round pays the player Nuggets worth the stake, so the prize is priced for it. With stake W, win chance p, house edge e and Nuggets cost c (a share of a Credit):

target = W × [(1 − e) − (1 − p) × c] / p
  • The operator still keeps e on average: p × target + (1 − p) × c × W = (1 − e) × W.
  • A win is always worth at least the stake, at the prize’s value (not its cashback): targetBand.lowerMinor is never below the stake.
  • A chance whose target would be worth less than the stake is not offered: maxChanceBps drops to 1 − e / (1 − c), and presets above it leave config.chancePresetsBps. If none is left, config.available is false.

With no Nuggets cost set, c is 0 and the band is [target / 1.1, target] with target = W × (1 − e) / p.

Play a round

POST /api/s2s/upgrader/play stakes the wager and plays one balance-mode round. LootBox Solutions calls your wallet.debit for the wager before the roll; a refused debit voids the round and moves no money. On a win the target item is granted (a normal inventory item, ready to cash back or ship).

POST https://{operator}.app.lootboxsolutions.com/api/s2s/upgrader/play
{
"playerExternalId": "u_8431",
"clientActionId": "5f2c…-per-click-uuid",
"wagerMinor": 10000,
"chanceBps": 2500,
"targetItemId": 906,
"currency": "EUR"
}
FieldRequiredNotes
playerExternalId✅The player to charge and credit.
clientActionId✅Idempotency key for this player action (≤80 chars). A retry with the same value returns the original round — your wallet is never debited twice.
wagerMinor✅The stake, in minor units of the display currency. Must be within the operator’s wager limits.
chanceBps✅The chosen win chance in basis points. Must be one of the offered presets (config.chancePresetsBps), at or under config.maxChanceBps.
targetItemId✅The chosen prize. Its value must fall in the band for this wager + chance — the server re-checks, so a client cannot request a richer prize than the odds allow.
currency—The currency to charge the player in. Defaults to the operator base; debited at the current rate when it differs.

Response — 200

{
"round": {
"publicId": "r_01J…",
"status": "settled",
"betMinor": 10000,
"payoutMinor": 38000,
"currency": "EUR",
"chanceBps": 2500,
"feeBps": 500,
"rolledBps": 1840,
"isWin": true,
"target": { "itemId": 906, "name": "…", "valueMinor": 38000, "currency": "EUR", "imageUrl": "https://…" },
"fairness": { "serverSeedHash": "…", "clientSeed": "…", "nonce": 42, "algorithmKey": "hmac_sha256_v1" },
"settledAt": "2026-06-24T12:00:05Z"
},
"wonItem": { "inventoryItemId": 88, "itemId": 906, "name": "…", "cashbackMinor": 30400, "cashbackUncredited": { "priceFallMinor": 0, "priceRiseMinor": 0, "creditsShareMinor": 0 }, "currency": "EUR", "state": "unresolved" },
"balanceAfterMinor": 78000,
"facts": { "currency": "EUR", "reels": [], "deal": { "stakeCountedMinor": 10000, "stakeUncredited": { "priceFallMinor": 0, "priceRiseMinor": 0, "creditsShareMinor": 0 } } }
}
FieldNotes
round.isWintrue when the roll landed in the win region.
round.rolledBpsThe roll as an integer in 0…9999; the round wins when rolledBps < chanceBps. Verify it against the fairness seeds.
round.payoutMinorThe granted value on a win, 0 on a loss.
round.targetThe prize the player aimed for (won on a win, missed on a loss).
wonItemThe granted inventory item, or null on a loss. cashbackMinor is what it could cash back for now (in the round’s player currency), null when not cashable; cashbackUncredited is the part of its value that cash-back would not pay in Credits, by cause (as on Cashout; all 0 unless the item is a collectible whose price moved or the operator pays less than 100% in Credits).
balanceAfterMinorThe casino’s authoritative balance after the debit, in the round currency.
  • Amounts are in the player’s currency — betMinor, payoutMinor, the target value, and the won item’s cashbackMinor come back in the currency the wallet was charged in (cashbackMinor at the round’s pinned rate — the actual cash-back reprices at claim time).
  • A win is a normal inventory item: cash it back via /cashout or ship it via /shipping — no Upgrader-specific endpoints.

Round facts

facts is the same block, with the same figures, that the round_settled webhook carries. For an Upgrader round facts.reels is empty and facts.deal holds the stake as counted (stakeCountedMinor, what a miss loses, in the player’s currency) and stakeUncredited, the part of the staked items’ value their stake did not count in Credits, by cause (see how a staked item is counted; all 0 for a Credits stake). Both play modes return it.

A refused debit returns the voided round and the casino’s error code:

{
"error": { "code": "INSUFFICIENT_FUNDS", "message": "…" },
"round": { "publicId": "r_01J…", "status": "voided" },
"balanceAfterMinor": 0
}

Wager-items mode

Instead of staking a balance amount, a player can stake owned inventory items — items they previously won and haven’t yet shipped or cashed back. The wager is the staked items’ combined value, and the staked items are consumed on play, win or lose (just as a balance stake is spent). On a win the target item is granted on top; on a loss the staked items are simply gone. No wallet call is made for the stake — the items are the stake.

Whether this mode is available, plus the win chances, house edge, and wager limits, are configured in your operator admin under Settings → Upgrader (the same controls as balance mode). The two endpoints below mirror the balance pair; only the stake is expressed as items rather than money.

List candidate prizes by items

GET /api/s2s/upgrader/prizes-by-items sizes the wager from the staked items’ combined value and returns the same band + in-band candidates as the balance prizes call, plus a stake echo of the items the wager was sized from. Read-only — nothing is staked or consumed here.

GET https://{operator}.app.lootboxsolutions.com/api/s2s/upgrader/prizes-by-items
?playerExternalId=u_8431&stakeItemIds[]=88&stakeItemIds[]=91&chanceBps=2500&currency=EUR
FieldRequiredNotes
playerExternalId✅The player whose items are staked. Every staked item must belong to this player.
stakeItemIds[]✅One or more inventory item ids to stake (the inventoryItemIds from /inventory). Each must be owned and still open — not already shipped or cashed back.
chanceBps✅The chosen win chance in basis points. Must be one of the offered presets (see config.chancePresetsBps).
currency—ISO-4217 display currency to price the band, prizes, and stake in. Defaults to the operator currency; must be one of your configured currencies.
locale—BCP-47-ish content locale. Any value is served, falling back to best-available copy.

Response — 200

The same shape as balance prizes — wagerMinor is the derived combined stake value (in the requested currency) — with one extra stake block:

{
"wagerMinor": 10000,
"chanceBps": 2500,
"feeBps": 500,
"currency": "EUR",
"targetBand": { "lowerMinor": 34545, "upperMinor": 38000 },
"prizes": [
{ "itemId": 906, "name": "…", "valueMinor": 38000, "currency": "EUR", "imageUrl": "https://…" }
],
"config": { "…": "…" },
"stake": {
"currency": "EUR",
"totalMinor": 10000,
"items": [
{ "inventoryItemId": 88, "itemId": 712, "name": "…", "valueMinor": 6000, "currency": "EUR" },
{ "inventoryItemId": 91, "itemId": 540, "name": "…", "valueMinor": 4000, "currency": "EUR" }
]
}
}
FieldNotes
wagerMinorThe combined value of the staked items, in the requested currency — the wager the band is computed from. The player doesn’t type it.
stake.totalMinorThe combined stake value; equals wagerMinor.
stake.items[]The staked items and the value each contributes, priced in the requested currency (see below).

While the items mode is off in Settings → Upgrader, this call and play with items answer UPGRADER_MODE_DISABLED (403).

How a staked item is counted

A staked item counts for exactly the Credits part of what cashing it back would pay at that moment, under the same collectible settings and cashback percentage (Catalog → Settings), so putting an item up and cashing it back first are always worth the same. The part of its value that cashback would not pay in Credits is reported on the play answer and on round_settled as facts.deal.stakeUncredited (priceFallMinor, priceRiseMinor, creditsShareMinor); whether you pay any of it in Nuggets when the item is put up is your choice. An item with no cashback of its own counts for its whole value at win time, all in Credits, and nothing is left out.

Play with items

POST /api/s2s/upgrader/play-with-items stakes the named items and plays one round. The staked items are consumed regardless of outcome — no wallet call is made for the stake. On a win the target item is granted (a normal inventory item, ready to cash back or ship) on top of the consumed stake.

POST https://{operator}.app.lootboxsolutions.com/api/s2s/upgrader/play-with-items
{
"playerExternalId": "u_8431",
"clientActionId": "5f2c…-per-click-uuid",
"stakeItemIds": [88, 91],
"chanceBps": 2500,
"targetItemId": 906,
"currency": "EUR"
}
FieldRequiredNotes
playerExternalId✅The player staking the items. Every staked item must belong to this player.
clientActionId✅Idempotency key for this player action (≤80 chars). A retry with the same value returns the original round — the stake is never consumed twice.
stakeItemIds✅The inventory item ids to stake (owned + open). Their combined value is the wager.
chanceBps✅The chosen win chance in basis points. Must be one of the offered presets (config.chancePresetsBps), at or under config.maxChanceBps.
targetItemId✅The chosen prize. Its value must fall in the band for the staked value + chance — the server re-checks.
currency—Display currency for the response amounts. Defaults to the operator currency. No money moves on the stake side, so this only affects how amounts are shown.

Response — 200

The same shape as balance play — the settled round and the granted wonItem (or null on a loss) — plus a stake block listing the consumed items, and balanceAfterMinor: null (no wallet leg moved):

{
"round": { "publicId": "r_01J…", "status": "settled", "betMinor": 10000, "payoutMinor": 38000, "isWin": true, "…": "…" },
"wonItem": { "inventoryItemId": 142, "itemId": 906, "name": "…", "cashbackMinor": 30400, "cashbackUncredited": { "priceFallMinor": 0, "priceRiseMinor": 0, "creditsShareMinor": 0 }, "currency": "EUR", "state": "unresolved" },
"stake": {
"currency": "EUR",
"totalMinor": 10000,
"items": [
{ "inventoryItemId": 88, "itemId": 712, "name": "…", "valueMinor": 6000, "currency": "EUR", "state": "consumed" },
{ "inventoryItemId": 91, "itemId": 540, "name": "…", "valueMinor": 4000, "currency": "EUR", "state": "consumed" }
]
},
"balanceAfterMinor": null,
"facts": { "currency": "EUR", "reels": [], "deal": { "stakeCountedMinor": 10000, "stakeUncredited": { "priceFallMinor": 0, "priceRiseMinor": 0, "creditsShareMinor": 0 } } }
}
FieldNotes
round.betMinorThe combined staked value — recorded as the round’s wager, so it counts toward loyalty / reporting exactly like a balance round.
stake.items[].stateconsumed — the staked items are spent and can no longer be shipped or cashed back, win or lose.
balanceAfterMinorAlways null — the stake is items, not money, so no wallet balance changes on play.

A win is a normal inventory item; cash it back or ship it via the same endpoints as any other inventory item.

Game-target launch

To open the Upgrader inside the iframe, mint a launch token with gameKey: "upgrader" and an empty target — it’s a game target (the whole game), not a box:

{ "playerExternalId": "u_8431", "gameKey": "upgrader", "target": {} }

The session boots straight into the Upgrader (game.key = upgrader), with its selector config on the bootstrap. A boxId is rejected — the Upgrader has no box to pin.

Errors

codeHTTPwhen
UPGRADER_MODE_DISABLED403the requested Upgrader wager mode (balance or items) is off
UPGRADER_WAGER_OUT_OF_RANGE422wager below the min or above the max (for items, the combined staked value)
UPGRADER_CHANCE_INVALID422chance isn’t a configured preset, or exceeds the chance cap (config.maxChanceBps)
UPGRADER_PRIZE_OUT_OF_RANGE422the target item isn’t an eligible prize, or its value is outside the band
UPGRADER_ITEM_NOT_OWNED422a staked item isn’t owned by the player, or isn’t available to stake (already shipped, cashed back, or spent, or bought with Nuggets)
GAME_DISABLED403the Upgrader isn’t in the operator’s enabled games
INSUFFICIENT_FUNDS422wallet refused — balance
PLAYER_BLOCKED403wallet refused — player state
WALLET_UNAVAILABLE503wallet timeout / 5xx / unconfigured
UNSUPPORTED_CURRENCY422currency not configured (or has no rate to price the round)
validation error422malformed input (e.g. a chance on prizes that isn’t in config.chancePresetsBps)

Same effect, two callers. The game app drives the equivalent in-iframe player-API upgrader/prizes + upgrader/play with a session token. This S2S pair is the backend-driven equivalent — same engine, same wallet calls, signed with your API key on behalf of a playerExternalId you supply.