Skip to content

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 }] }
  • clientActionId is required (≤80 chars). The game app generates a UUID per click and caches it in sessionStorage, so a mid-flight reload reuses it and the wallet is never debited twice for one click.
  • reels is 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 more unresolved items with an address + 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; clientSeed in 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).