# Game actions > The launcher-side endpoints for every game: Mines, Magic Ball, Spin da’ Bottle, Under Over 7, Dice, Lucky Wheel, Twin Wheels, Prize Drum, Gem Rush, Chilli Sevens, Big Top Coins, Prism Sevens, Nova Drop and Goal Jet. Source: https://casinoapipro.com/docs/game-actions These endpoints are called by the game launcher in the player's browser, with the short-lived token from the launch URL — not by your server. They are documented because you will see the resulting rounds and transactions, and because some operators build their own front end. > **You usually do not call these** > > Open the launch URL and our launcher handles all of it. Read on only if you are building a custom client against `/v1/game/*`. ## Session state `GET /v1/game/state` — auth: game-launcher Everything a client needs to render: the session, the player's balance as your wallet last reported it, the stake limits, and the current fairness commitment. ## The generic action endpoint `POST /v1/game/play/{action}` — auth: game-launcher Every single-player game is driven through one endpoint. Adding a game does not add an endpoint — the action name and body are defined by the game's own contract, which is why our catalogue can grow without your client changing. ## Mines ### Start a round `POST /v1/game/play/start` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake. Debited before the grid is generated. | | `mines` | integer | yes | How many mines on the 5×5 grid. More mines, higher multipliers. | ```json { "round_id": "rnd_7c31ea", "state": "ACTIVE", "revealed": [], "multiplier": "1.00", "balance": "489.50" } ``` ### Pick a tile `POST /v1/game/play/pick` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `tile` | integer | yes | 1–25. Picking a tile that is already open is refused. | A safe tile raises the multiplier. A mine ends the round at zero and reveals the whole grid, so the player can verify the layout against the seed. ### Cash out `POST /v1/game/play/cashout` — auth: game-launcher Settles at the current multiplier and credits the win through your wallet. Cashing out with nothing revealed is refused. ## Magic Ball `POST /v1/game/play/play` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stakes` | decimal string[3] | yes | One stake per cup, in order. `"0"` means no chip on that cup — the player is not picking one cup, they are placing chips, and may cover more than one. | | `client_seed` | string | no | Optional. Folded into the fairness commitment for this round. | At least one cup must carry a chip, and no more than `maxSpots` of them (two by default) — covering all three would be a stake that cannot lose, which is not a game. ## Spin da’ Bottle `POST /v1/game/play/spin` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake. | | `pick` | string | yes | `"up"` or `"down"`. Settled on the spin. | | `client_seed` | string | no | Optional. Folded into the fairness commitment for this round. | ## Under Over 7 `POST /v1/game/play/roll` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake. | | `pick` | string | yes | `"under"` (2–6), `"seven"` (exactly 7) or `"over"` (8–12). | Two dice, settled immediately. The response carries both `dice`, their `total`, the `outcome` that total fell into, and the `multiplier` the chosen call pays. > **Every call returns the same, so the seven is not a worse bet** > > Two dice give 15 ways in 36 for either range and 6 for the seven, and each multiplier is derived from its own odds and the configured return — 2.32× and 5.82× at 97%. The cabinets this game comes from pay a flat 2×/3×/2×, which returns 83.3% on a range and 50% on the seven: the bet that looks exciting is the worst on the table. Show your players the chance beside the multiplier and neither is a trap. ```json { "round_id": "rnd_8q2v…", "dice": [3, 4], "total": 7, "outcome": "seven", "pick": "seven", "multiplier": "5.82", "status": "won", "stake": "1.00", "payout": "5.82" } ``` ## Dice `POST /v1/game/play/roll` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake. | | `target` | integer | yes | Win under this. Bounded by the release’s `minTarget` and `maxTarget`, 2–98 by default. | A number in 0.00–99.99 is drawn and you win if it comes in strictly under your target, settled immediately. The response carries the `roll`, the `target`, the `multiplier` that target pays and its `win_chance`. > **Every target returns the same, so a long shot is not a worse bet** > > The multiplier is the configured return divided by the target’s own odds, floored to two places — 97 ÷ 50 = 1.94× at a target of 50, and 97 ÷ 2 = 48.5× at a target of 2. A player picking a long shot is choosing variance, not a worse price, and showing the win chance beside the multiplier is what makes that visible. ```json { "round_id": "rnd_3k9m…", "roll": "42.17", "target": 50, "multiplier": "1.94", "win_chance": "50%", "status": "won", "stake": "1.00", "payout": "1.94" } ``` ## Lucky Wheel `POST /v1/game/play/spin` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake. Every prize is a multiple of it. | Sixteen faces, one draw, settled immediately. The response carries the `segment` that came up, its `multiplier`, and the `angle` the rim stopped at — the last only so a client can animate to the answer the server already gave. > **The faces are equal and the chances are not** > > Sixteen equal chances at this ladder would return 2352%, so the wheel is weighted. The weights are part of the certified release, come back on `GET /v1/game/state` as `config.segments`, and are what a player re-derives when verifying a round. A client drawing its own ladder would eventually draw one the engine had stopped using — read them from the session. ```json { "round_id": "rnd_ipskzzin6dxe2f5vmr6k", "segment": 7, "multiplier": "0.6", "angle": "168.7500", "status": "won", "stake": "2.00", "payout": "1.20", "server_seed_hash": "2040c88d…", "nonce": "1" } ``` ## Twin Wheels `POST /v1/game/play/spin` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `bets` | object | yes | Multiplier to stake, as decimal strings — any of `"2"`, `"4"`, `"6"`, `"15"`, `"24"`, `"54"`. Cover as many as you like; the round's stake is their total. Each must be at least the table minimum. | | `free_bet_grant_id` | string | no | Fund the round from a free bet. The bets must then total exactly the grantu2019s stake, or the round is refused and the free bet is kept. | Two wheels of twenty-five faces each stop on one face apiece, and the pair settles to one multiplier: a match pays itself, a Wild becomes the other wheel's multiplier, two different multipliers pay x2, and two Wilds re-spin free with every bet still riding. The stake on the multiplier that lands is paid at that multiplier; the others are lost. `spins` lists every spin the round took — more than one only when Wilds re-spun — as face indexes on each rim, so a client can animate each in turn. `outcome` is the multiplier that settled. > **The faces are the odds** > > Nothing is weighted: each wheel is twenty-five equal faces — five Wild, eight x4, six x6, three x15, two x24, one x54 — and every pair of faces is equally likely. They are part of the certified release and come back on `GET /v1/game/state` as `config.rimA` and `config.rimB`. Of 625 pairs, 25 re-spin and the other 600 settle: x2 286, x4 144, x6 96, x15 39, x24 24, x54 11. Every bet returns between 95.33% and 99.0%, and the game publishes the lowest. Its return is not an operator setting, because the payouts are printed on the faces and the odds are the faces. ```json { "round_id": "rnd_4w8cqk2j0hz1n7yd5ex3", "spins": [ { "a": 0, "b": 0, "face_a": "W", "face_b": "W" }, { "a": 3, "b": 0, "face_a": "15", "face_b": "W" } ], "free_spins": 1, "outcome": "15", "multiplier": "15", "status": "won", "stake": "2.00", "payout": "15.00", "server_seed_hash": "9d1f30a2…", "nonce": "4" } ``` ## Prize Drum `POST /v1/game/play/spin` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `bets` | object | yes | Colour multiple to stake, as decimal strings — any of `"40"`, `"20"`, `"10"`, `"5"`, `"4"`, `"2"`. Back any or all; the round's stake is their total. | | `free_bet_grant_id` | string | no | Fund the round from a free bet. The bets must then total exactly the grantu2019s stake. | A drum of fifty equal sectors. A colour sector pays the stake on that colour at its multiple; a free-spin sector grants four spins, two, or one (a respin), played straight away with the same bets at no cost — and they can grant more. `spins` lists every spin of the round in order, each with the `sector` it landed on, its `face` (`"40"`…`"2"`, `"F4"`, `"F2"` or `"R"`) and what it `payout`; the round's `payout` is their sum. > **Every colour returns exactly 40/41** > > Colours are counted in proportion to 1/multiple — x40 1, x20 2, x10 4, x5 8, x4 10, x2 20 — so each carries the same weight, and with one four-spin sector, one two-spin sector and three respins every colour returns 40/41 = 97.56%, free spins included. The sectors are in the certified release and come back on `GET /v1/game/state` as `config.sectors`. The return is not an operator setting: the multiples are printed on the colours and the odds are the sectors. A round is capped at 100 spins, which no round will reach in practice. ```json { "round_id": "rnd_t0m8a3zq6c1v4h7n2wxe", "spins": [ { "sector": 34, "face": "F2", "payout": "0" }, { "sector": 8, "face": "10", "payout": "10" }, { "sector": 4, "face": "40", "payout": "40" } ], "free_spins": 2, "status": "won", "stake": "2.00", "payout": "50.00", "server_seed_hash": "4e2b77c0…", "nonce": "9" } ``` ## Gem Rush Three actions on one round, the way Mines has them. `dig` opens it and takes the stake. A dig that pays nothing settles there; one that pays keeps the round open with the win on the table, until `collect` pays it or `gamble` doubles or loses it. Your wallet sees one debit and, at most, one credit — the gamble is not a chain of rounds. ### Dig `POST /v1/game/play/dig` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `bets` | object | yes | Gem to stake, as decimal strings — any of `diamond`, `amber`, `rose`, `sapphire`, `quartz`, `ruby`, `topaz`, `emerald`. Back any or all; the round's stake is their total. | | `free_bet_grant_id` | string | no | Fund the round from a free bet. The bets must then total exactly the grantu2019s stake. | The rock gives up one gem — or, one dig in twenty, TNT (`tnt: true`) gives three, four or five, each drawn independently. `finds` lists them in order, each with its `gem`, the `multiplier` drawn from that gem's own list, and its `payout`: the stake on that gem times the multiplier. `win` is their sum. `status` is `"lost"` (settled, nothing found that was backed), `"won"` (open, the win is on the table) or `"collected"` (settled at once, because the release has the gamble off or the win cannot be doubled under the maximum win). `can_gamble` says whether a call is allowed. ```json { "round_id": "rnd_g3m5r8u2s4h6x1k9q7wz", "tnt": true, "finds": [ { "gem": "ruby", "multiplier": 10, "payout": "10.00" }, { "gem": "emerald", "multiplier": 3, "payout": "3.00" }, { "gem": "amber", "multiplier": 3, "payout": "0.00" } ], "win": "13.00", "steps": 0, "can_gamble": true, "status": "won", "stake": "2.00", "settled": false, "server_seed_hash": "b71e09d4…", "nonce": "12" } ``` ### Gamble `POST /v1/game/play/gamble` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `call` | string | yes | `"small"` (1–7) or `"big"` (8–14). | A number from 1 to 14 is drawn. Right doubles `win` and the round stays open; wrong settles it at nothing. The response carries the `number`, whether the call was `correct`, and the new `win`, `steps` and `can_gamble`. A win may be doubled up to `gambleSteps` times (five by default) and never past the maximum win; past either, a call is refused and the win must be collected. ### Collect `POST /v1/game/play/collect` — auth: game-launcher Settles the round at `win` and credits it through your wallet. A win left on the table is not lost: the round stays open, `GET /v1/game/state` returns it under `round.state`, and the launcher offers the same choice when the player comes back. > **Every gem returns at least 97.51%, and the gamble changes nothing** > > Gems are drawn by weight out of 100 — diamond 1, amber, rose and sapphire 8 each, quartz, ruby and topaz 16 each, emerald 27 — and each multiplier by weight out of 1000. A dig expects 227/200 gems. A stake on any gem returns 97.519% or 97.528%, and on the emerald 97.819%; the game publishes the lowest. The gamble is an even call at even money, so it moves the risk and never the return. The number each call is judged against is drawn from the round's seed at a fixed index, before the call is made — a player cannot re-roll it. The return is not an operator setting, because the multipliers are printed on the gems; a release can switch the gamble off with `gamble: false` where a double-or-nothing feature is not allowed. ## Prism Sevens `POST /v1/game/play/spin` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake for the spin: it covers five lines. 0.20–20.00 by default. | | `free_bet_grant_id` | string | no | Fund the spin from a free bet, at exactly the grantu2019s stake. | Three reels, three rows, five lines — the rows and the two diagonals. A line pays for three sevens of one colour; a window full of gold, red or green sevens also pays the Grand (1000×), Major (100×) or Mini (25×) jackpot as `jackpot`. `scatters` counts the scatters in view: three start ten free spins, which come back played, in order, as `free_spins` — each with its own `grid`, `lines`, `jackpot` and `win`. Three scatters during them add ten more, up to a hundred. `payout` is the spin and every free spin together. > **The reel strips are the odds, and the return is counted** > > Each reel is thirty stops: 3 gold, 3 red, 4 green, 2 pink, 2 blue, 2 cyan, 5 purple, 7 white and 2 scatters, returned on `GET /v1/game/state` as `config.reels`. Lines are a product of the three reels' counts; gold, red and green each stand in one stack of three, so a full window is exactly 1 in 27,000; three scatters are 1 in 125 and a feature expects 10 / (1 − 10/125) free spins. Altogether 96.0548%, published as 96.054. ## Big Top Coins A five-line slot with a hold-and-win coin bonus, and two ways to play beyond a plain spin. Both actions settle at once: the whole bonus is played out by the engine and returned for the client to show. ### Spin `POST /v1/game/play/spin` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake. Wins are paid on it. 0.20–5.00 by default. | | `ante` | boolean | no | The ante bet: the spin costs 1.5 stakes and the centre reel carries a second Clown, doubling the bonus's chance. | | `free_bet_grant_id` | string | no | Fund the spin from a free bet, at exactly the grantu2019s stake and without the ante. | `grid` shows the five reels, `lines` what paid (in stakes), and `coins` the coins in view with their values. With the Clown and two or more coins in view, `bonus` carries the bonus: the `start` coins by place (0–11 across reels 1, 2, 4 and 5), every respin with what `landed`, which cash coins a Lucky Clown `multiplied` and by how much, and how many respins were `left`; then the `total` in stakes and the `win`. The round's stake is what the spin cost. ### Buy the bonus `POST /v1/game/play/buy` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake. The bonus costs 70 times it, and pays on it. | Starts the bonus at once, with two, three or four coins in the proportions a natural trigger has (486 : 36 : 1). Free bets cannot buy it. > **Three returns, each counted exactly, and the lowest published** > > Lines are a sum over every combination of symbols. A trigger is a product of independent reels, because no window shows two coins or two Clowns. The bonus's mean depends only on how many places are filled and how many respins remain — coin values and Lucky Clown multipliers carry through by linearity — so a recursion over those two numbers gives it exactly: 67.05 stakes. Spinning returns 96.110%, with the ante 96.406%, buying 95.792%; the game publishes 95.792. The strips come back on `GET /v1/game/state` as `config.reels` and `config.anteCentre`. The return is not an operator setting. ## Chilli Sevens `POST /v1/game/play/spin` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake for the whole spin. It covers all twenty lines; each line is staked a twentieth of it. 0.20–5.00 by default. | | `free_bet_grant_id` | string | no | Fund the spin from a free bet. The stake must then be exactly the grantu2019s. | A five-reel, twenty-line fruit machine, settled immediately. `stops` is where each reel stopped (the index of the symbol in its top row) and `grid` the five columns of three symbols that shows. `lines` lists every paying line — its number, the `symbol`, how many in a row, and what it `pays` in line stakes. `stars` counts scatters anywhere. `chillies` are the three chilli reels (`0` blank, otherwise how many chillies); three of the same make `multiplier` 3, 6 or 9, applied to the line and star wins in `spin_win`. One spin in 500 carries `jackpot`: its `kind` (mini, minor, major, mega or grand), what it `pays` as a multiple of the stake, the `win`, and `crystals` — the order the crystal board reveals, ending on the third of the winning kind. The jackpot is decided when the spin is played; a client reveals the sequence, and never lets a pick choose it. `payout` is `spin_win` plus the jackpot. > **The reel strips are the odds, and the return is counted, not simulated** > > Each reel is thirty stops — three stacked 7s, one star, three cherries, three melons, four lemons, four grapes, six oranges, six plums — in its own order, and they come back on `GET /v1/game/state` as `config.reels`. A line reads one row from each reel, so its return is a sum over every combination of symbols: 73.914% from lines, 2.12% from stars, times the chilli multiplier's mean of 1.176, plus 6.78% from the crystal board — 96.196% in all, published floored as 96.195. The return is not an operator setting. At the default limits a Grand jackpot is exactly the 50000 maximum win, so the figure holds at every stake. ```json { "round_id": "rnd_c7h1l2s3e4v5n6r7t8y9", "stops": [12, 4, 27, 9, 18], "grid": [["cherry", "plum", "seven"], ["cherry", "orange", "lemon"], ["cherry", "grape", "melon"], ["star", "melon", "plum"], ["lemon", "orange", "grape"]], "lines": [{ "line": 2, "symbol": "cherry", "count": 3, "pays": 6 }], "stars": 1, "chillies": [2, 2, 2], "multiplier": 6, "spin_win": "1.80", "jackpot": null, "status": "won", "stake": "1.00", "payout": "1.80", "server_seed_hash": "e0a4c91b…", "nonce": "31" } ``` ## Nova Drop `POST /v1/game/play/drop` — auth: game-launcher **Body** | Name | Type | Required | Description | | --- | --- | --- | --- | | `stake` | decimal string | yes | The stake. Every prize is a multiple of it. | | `rows` | integer | no | Rows of pegs, `8`–`16`. Defaults to `8`. | | `risk` | string | no | `"low"`, `"medium"` or `"high"`. Defaults to `"medium"`. | | `balls` | integer | no | Balls in this drop, `1`–`10`. The stake is charged per ball. Defaults to `1`. | A ball falls through a triangle of pegs into the slots below it, settled immediately. The response carries the `slot` it landed in, that slot’s `multiplier`, the `rows` and `risk` the round was played at, and — unlike every other game here — the whole `path`. > **The seed reproduces the route, not just the result** > > Every other game draws one number and maps it to an outcome. This one draws one per row, so the revealed seed reproduces which side of every peg the ball took, in order. `path` is a string of `L` and `R`, one character per row, and the slot is simply the number of `R`s — a player can verify the bounce as well as the payout, and a board that dropped a ball down a path its seed does not generate would be caught by anyone who looked. > **Risk changes the payouts and never the odds** > > The chance of reaching a slot is Pascal’s row for the row count — eight rows give 1:8:28:56:70:56:28:8:1 out of 256 — and it is identical on all three risk settings. Risk moves only what each slot pays, and the return is the same on every setting, so higher risk is more variance and not a worse bet. Say so in your UI: the number that sells the game is the corner multiplier, and the number that makes it a decision is the chance beside it. > **A flight is one round, and the stake is per ball** > > `balls: 10` is ten bets in one round: the round is charged ten times the stake, and it returns what all ten balls made. Do not make ten calls — a player may hold one open round per game, so concurrent drops are refused with `ROUND_IN_PROGRESS`. One round also means one wallet debit and one credit rather than ten of each. Show the TOTAL in your UI. Ten balls at a stake is ten times the risk of one, and a panel displaying only the per-ball figure is telling a player they are risking a tenth of what they are. Each ball still draws from its own indices — ball `b`, row `i` uses `b × rows + i` — so every ball in a flight stays independently verifiable, and `balls[]` carries each one’s path, slot, multiplier and payout. The first ball is repeated at the top level so an integration written before flights existed keeps working unchanged. > **Offer only the boards your release certifies** > > A release carries every board a player may choose, keyed `rows:risk`, and comes back on `GET /v1/game/state` as `config.boards`. A request for a combination the release does not carry is refused rather than settled against a ladder nobody signed off. Read the keys from the session rather than assuming the full 8–16 range. ```json { "round_id": "rnd_e5jfco5u72zkwkosvpmn", "path": "RRRRLRLR", "slot": 6, "rows": 8, "risk": "medium", "multiplier": "1.01", "balls": [ { "path": "RRRRLRLR", "slot": 6, "multiplier": "1.01", "payout": "1.01" }, { "path": "LRRLRRRL", "slot": 5, "multiplier": "0.503", "payout": "0.50" }, { "path": "RRLRRRRR", "slot": 7, "multiplier": "3.52", "payout": "3.52" } ], "status": "won", "stake": "3.00", "payout": "5.03", "server_seed_hash": "863b48d8…", "nonce": "1" } ``` ## Goal Jet — a shared round Crash games work differently: one flight is shared by everyone playing at your casino, so the round is not owned by a single player. Stakes go on before it starts; each player cashes out on their own. > **Your flight is yours** > > Each operator flies their own. Your players share a flight with each other and with nobody else, its crash points are computed from the RTP _you_ set, and its round numbers count within your casino. A flight starts when your first player arrives rather than running against an empty table. `GET /v1/game/crash/state` — auth: game-launcher `POST /v1/game/crash/bet` — auth: game-launcher `POST /v1/game/crash/cashout` — auth: game-launcher - A player may hold more than one stake on the same flight. - Cash-out moves through `ACTIVE → SETTLING → WON`. If your wallet times out mid-settlement, the retry resumes at the _same_ multiplier — a player can never be shown a win we then fail to pay. - The flight's crash point is committed before betting opens and verifiable afterwards, exactly as in [Provably fair](/docs/provably-fair). ### A flight sleeps when nobody is playing It stops after about two minutes without a player and wakes on the next sign of life — which now includes `POST /v1/sessions`, so by the time your player's launcher connects the round is already open. Flying empty tables for every operator every quarter second is waste, and this is how it is avoided. While it is asleep the last round stays in the table exactly as it ended, so `/crash/state` tells you which you are looking at: | Name | Type | Meaning | | --- | --- | --- | | `flight_status: "running"` | field | The round below is the live one. | | `flight_status: "starting"` | field | Nothing was flying. Reading the state has woken it — poll again and there will be a round. Do not offer a stake against the round in this response: it has already settled and the bet will be refused with `ROUND_ALREADY_SETTLED`. | | `flight_idle_seconds` | field | How long it had been asleep. Null when running. | > **This was missing until 14 September 2026** > > Neither field existed, so a client could not tell a round that crashed five seconds ago from one that crashed eight days ago. An operator's players were shown the latter and their stakes refused against it, and nothing in the catalogue, the session response or the launch URL said the flight was not running. They found it by reading their own logs. To check without starting anything — and without minting a session you may not use — use `GET /v1/catalogue/{gameId}/flight` on the operator API. Reading it does not wake the flight. See [running it from your own servers](/docs/automation). ## Round history `GET /v1/game/history` — auth: game-launcher This player's recent rounds in this game, with the fairness record attached to each so the client can offer a verify button without another round trip.