Player API
The player API (/api/play/*) is the in-iframe surface. The
embedded game app drives it for you — it’s
documented here for diagnostics, and so you can build a native client if you
ever want one. All routes (except session/init) require
Authorization: Bearer <sessionToken>. CORS is open on /api/play/*.
For backend-driven integrations, the S2S API offers the same effects (purchase / cashout / cancel / history) signed with your API key.
Session
POST /api/play/session/init
Body { "launchToken": "lt_…" }. Exchanges a launch token
for a session token and the bootstrap. The token is consumed; a second call
returns LAUNCH_TOKEN_CONSUMED.
{ "sessionToken": "ps_…", "expiresAt": "…", "player": { "externalId": "u_8431", "balanceMinor": 12000, "balanceFetchedAt": "…" }, "tenant": { "name": "<your brand>", "locale": "en", "currency": "EUR", "logoUrl": "…" }, "theme": { "tokens": { "brandColor": "#3b82f6" } }, "game": { "key": "mystery_box", "maxReels": 1, "target": { "boxId": 42, "boxVersionId": 1201, "name": "Dragon Box", "priceMinor": 500, "currency": "EUR", "freeOpens": 1 } }, "fairness": { "serverSeedHash": "…", "clientSeed": "…", "nextNonce": 42, "algorithmKey": "hmac_sha256_v1" }, "policies": { "cashbackEnabled": true, "shippingEnabled": true, "refundEnabled": true }}The bootstrap is frozen for the session. balanceMinor may be null if the
wallet balance couldn’t be read — a null balance never blocks play.
game.target.freeOpens is the number of operator-funded opens this box page
advertises — set when the launch was minted with funding: "operator" (and
fundingRounds, if capped); 0 for a normal
paid box. While it’s above zero the game app shows an “Open your free box” CTA
and skips the balance check; it counts down locally as each free open settles,
reverting to the paid price once spent.
The policies block tells the game app which actions are available this session,
so it can show or hide controls up front:
cashbackEnabled— a won item may be cashed back to the wallet (cashout).shippingEnabled— won physical items may be shipped (fulfillment).refundEnabled— a pre-bought, unopened box may be cancelled and refunded (cancel), per the operator/box refund policy. Already-opened rounds are never refundable, so this only affects vault boxes awaiting their auto-open.
POST /api/play/session/heartbeat
Bumps idle expiry (default 30 min) and refreshes balance. Returns
{ expiresAt, balanceMinor, balanceFetchedAt }. A wallet failure nulls the
balance fields without erroring.
POST /api/play/session/logout
Revokes the session. Idempotent (204).
Open a round
POST /api/play/rounds/open
{ "clientActionId": "<uuid-per-click>", "reels": [{ "reelIndex": 0, "boxVersionId": 1201 }] }clientActionIdis required (≤80 chars). The game app generates a UUID per click and caches it insessionStorage, so a mid-flight reload reuses it and the wallet is never debited twice for one click.reelsis a non-empty array;reelIndex≥ 0,boxVersionId≥ 1.
// settled{ "round": { "publicId": "r_…", "status": "settled", "betMinor": 500, "payoutMinor": 320 }, "balanceAfterMinor": 11680 }
// debit refused → voided{ "error": { "code": "INSUFFICIENT_FUNDS" }, "round": { "publicId": "r_…", "status": "voided" }, "balanceAfterMinor": 0 }(Pre-pay/vault opens follow the same response shapes; see Purchase for the held-box flow.)
Inventory
GET /api/play/inventory?state=<state>[,<state>…]
Won items for the session player. States: unresolved, ship_requested,
fulfilled, cashed_back, expired, forwarded_to_casino. Cursor-paginated
{ items: [...], cursor: { next, prev } }. Always scoped to the session player.
Cash back an item
POST /api/play/inventory/{id}/cashback — sync wallet credit then state flip to
cashed_back. Only unresolved items qualify. Failure leaves the item
untouched and returns the casino’s error code. (S2S equivalent:
Cashout.)
Shipping
POST /api/play/shipping/orders— request shipping for one or moreunresolveditems with anaddress+contact. All-or-nothing: a non-shippable item rejects the whole batch (INVENTORY_NOT_SHIPPABLE). If shipping cost > 0, the wallet is debited first.GET /api/play/shipping/orders/GET /api/play/shipping/orders/{publicId}— the player’s orders, by opaque public id (fo_…).
Fairness
GET /api/play/fairness/state— the current commitment.POST /api/play/fairness/rotate— reveal the current server seed and rotate to a new pair;clientSeedin the body sets the new seed (omit to keep the previous). See Fairness & verification.
Wallet
POST /api/play/wallet/balance
Explicit balance refresh; 503 on wallet failure (no server-side caching).