Skip to content

Operator API reference

Every endpoint below requires a valid operator signature (see Signing & authentication) and is rate-limited per tenant. See Errors & retry for the full status-code reference shared across this API — this page covers only what's specific to each endpoint.

POST /v1/operator/games/launch

Request body:

ts
type LaunchGameRequestBody = {
  playerRef: string
  gameId: string
  currency: string // must be one of the target game's GameSummary.currencies
  mode: 'REAL' | 'DEMO'
  language?: string // optional BCP-47 tag (e.g. "en", "zh-TW"); must be in the game's GameSummary.supportedLanguages if given — omit to let the game fall back to its own default
  lobbyUrl?: string // optional absolute http(s) URL; appended to launchUrl as `&lobby=` — the game's "back to lobby" control navigates the top window here instead of asking the shell to close it. Omit for the existing shell-close behavior.
}

Response:

ts
type LaunchGameResponse = {
  launchUrl: string     // embed this in an iframe
  sessionToken: string  // also embedded in launchUrl; the provider's game
                          // client presents this on its own wallet calls
}

Error responses (in addition to the shared set in Errors & retry): 404 (game not found, or not visible to your operator tenant — DEMO-mode launches skip the visibility check, so this only applies to REAL), 400 (currency not supported by the game, language given but not in the game's supportedLanguages, or lobbyUrl given but not an absolute http(s) URL), 403 (the launch was blocked by risk control — the player is on this operator's blocklist; the response body carries only a generic message, not the specific reason — contact the platform admin team if you need to know why a specific player was blocked).

GET /v1/operator/games

No request body. Returns the games visible to your operator tenant.

GameSummary

ts
type GameSummary = {
  gameId: string
  name: string
  provider: string
  currencies: string[] // ISO-4217 codes this game's provider supports — shared by every game that provider owns, not set per game; the set LaunchGameRequestBody.currency is validated against
  supportedLanguages: string[] // BCP-47 tags (e.g. ["en", "zh-TW"]) this game's provider supports — the set LaunchGameRequestBody.language is validated against
  launchBaseUrl: string // the provider's game client URL; launch appends the session token to it
  replayBaseUrl: string // the provider's replay page URL, empty if the game doesn't support round replay — see POST /v1/operator/rounds/replay below
}

Response: GameSummary[].

POST /v1/operator/players/kick

Ends every active session playerRef has under your operator tenant — use this to force a player out (e.g. self-exclusion, a fraud hold, an account freeze). This is authoritative immediately: the session is deleted server-side, so the player's game client fails its very next wallet call with 401. The platform additionally best-effort notifies each ended session's provider so a still-open game client can be pulled down right away instead of waiting for that next call to fail — but a provider that can't be reached doesn't change this endpoint's result, and the player's browser only finds out through the shell bridge's SESSION_REVOKED event, which you report, not through anything the platform pushes to the browser directly.

Request body:

ts
type KickPlayerRequestBody = {
  playerRef: string
}

Response:

ts
type KickPlayerResponse = {
  revokedCount: number // how many sessions were actually ended — 0 if the player had none active, which is success, not an error
}

Error responses: 400 (missing playerRef), 401 (no operator identity resolved), 429 (rate limit exceeded), 500 (unexpected failure).

POST /v1/operator/rounds/replay

Mints a link to a provider-hosted page that replays a specific round — see Getting a round replay link for the full picture (what "replay" means, and what a provider needs to implement for it to work). The platform never sees a round's visual outcome, only its money movements — replay support is opt-in per game (GameSummary. replayBaseUrl is empty when it isn't configured), and only rounds played after a provider adds recording are replayable.

Request body:

ts
type RoundReplayRequestBody = {
  roundId: string // the same roundId the provider's transactions for this round were submitted under
  gameId: string
}

Response:

ts
type RoundReplayResponse = {
  replayUrl: string // open this URL in a browser to watch the round replay
}

replayUrl embeds a short-lived, single-purpose token (the same pattern as launchUrl's session token) — the link expires after a fixed window (15 minutes by default), so request a fresh one each time rather than caching it.

Error responses: 404 (no such round for this roundId + gameId under your operator tenant — including a roundId that belongs to a different operator or a different one of your own games, which is deliberately indistinguishable from "doesn't exist" so this endpoint can't be used to probe for round IDs you don't own), 400 (roundId/gameId missing, or the game doesn't support replay — its replayBaseUrl isn't configured), 401 (no operator identity resolved), 429 (rate limit exceeded), 500 (unexpected failure).

Free spins

Operator self-serve free-spin grants live on their own routes — POST /v1/operator/free-spins/grants and friends — documented separately in the Free spins API reference, since they carry their own idempotency and status model. See Free spins for the guide.