# Rounds & history > Your rounds, movements, sessions, players and free bets — listed, for your servers. Source: https://casinoapipro.com/docs/history Your own rounds, players, sessions, movements and free bets, listed — the same records the dashboard shows, for your servers rather than a person in a browser. Everything here is read with your API credentials and scoped to the calling key's environment. > **Anything you can read here, you can automate** > > These exist because the split was never a decision. The dashboard grew these screens first, and a server had no way to ask the same questions — `GET /v1/rounds` answered 404 to operators building reconcilers against their own data. If something is still missing, tell us; the gap is an accident, not a policy. ## Paging and windows Every list here takes `limit` (1–200, default 50) and `cursor`, and returns `data`, `next_cursor` and `has_more`. Follow `next_cursor` while `has_more` is true. Most also take `from` and `to`, inclusive and UTC. There is no `environment` parameter, deliberately: a credential belongs to one environment and the history you get is its own. A test key cannot read live records. ## Rounds `GET /v1/rounds` — auth: operator-api **Query** | Name | Type | Meaning | | --- | --- | --- | | `player_id` | string | Your own player id. | | `game` | string | Game slug, e.g. `mines`. | | `status` | string | `OPEN`, `SETTLED` or `VOIDED`. | | `free_bet` | true | false | Rounds funded by a free bet — the ones that raise no wallet call. | | `from / to` | ISO 8601 | On the round start time. | ```json { "round_id": "rnd_45vq2tibskftpz9f6xto", "wallet_round_id": "4316deee-b478-434d-a5a7-51a4a6523c37", "wallet_game_id": "deef2e01-f65d-4dad-8cc4-5eeebf75b77b", "external_round_id": null, "player_id": "your_player_42", "game": "spin-bottle", "game_name": "Spin da' Bottle", "status": "SETTLED", "stake": "1.00", "total_bet": "1.00", "total_win": "2.00", "currency": "GHS", "free_bet": false, "outcome": { "angle": "223.9624", "status": "won", "outcome": "up" }, "fairness": { "server_seed_hash": "a02540d7…", "client_seed": null, "nonce": 1 }, "started_at": "2026-09-02T21:15:10.341Z", "completed_at": "2026-09-02T21:15:10.439Z" } ``` **Two fields worth reading twice** | Name | Type | Meaning | | --- | --- | --- | | `stake vs total_bet` | they differ | `stake` is what was staked. `total_bet` is summed from settled BET rows, so it is `"0.00"` on a free bet — no debit was raised. Reconcile against `stake`. | | `free_bet` | boolean | True means no wallet call exists for this round's stake. A losing free bet has no wallet call at all — see [webhooks](/docs/webhooks). | ## One round, and what it moved `GET /v1/rounds/{roundId}` — auth: operator-api Accepts any of the three identifiers for a round — the `rnd_…` handle events carry, the uuid wallet calls carry, or your own `external_round_id`. Returns the object above plus the movements the round caused, so a reconciler settles it in one call: ```json "transactions": [ { "type": "BET", "status": "SUCCESS", "amount": "1.00", "transaction_id": "rnd_45vq2tibskftpz9f6xto.bet", "created_at": "…" }, { "type": "WIN", "status": "SUCCESS", "amount": "2.00", "transaction_id": "rnd_45vq2tibskftpz9f6xto.win", "created_at": "…" } ] ``` > **An empty list is not a fault** > > A settled round with no transactions is a losing free bet: nothing was owed and nothing was called. That is the case this endpoint exists to let you prove. ## Voiding a stuck round `POST /v1/rounds/{roundId}/void` — auth: operator-api A round is created before its stake is taken, so if the debit fails the round can be left `OPEN`. A player may hold one open round per game — a round open in Mines never blocks Dice — so a stranded round locks them out of that one game until it is cleared. This releases it. ```bash curl -X POST https://api.casinoapipro.com/v1/rounds/rnd_8mmde7j8/void \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"reason": "debit never completed"}' ``` > **Only for rounds that never took money** > > A round with a `SUCCESS` or `PENDING` movement against it is refused with `409`. Those moved real money, or may have, and the answer is a [refund or rollback](/docs/transactions) — which reverses it on both sides — rather than a status change that would strand the difference. Movements that `FAILED` moved nothing and do not block a void. A round staked with a free bet is refused too: the free bet did leave the player, and the round is still in play — it settles on its own. Voiding moves no money in either direction. Once voided, the round is closed for good: a later bet or win naming the same `round_id` is refused with `ROUND_ALREADY_SETTLED`, because reporting leaves voided rounds out and money attached to one would show in no figure. The same action is in the dashboard, under Activity → Rounds: open an `OPEN` round and it offers Void only where this endpoint would accept it. There a reason is required, and it is kept in the audit log with the name of whoever pressed it. ## Movements `GET /v1/transactions` — auth: operator-api The ledger, listed. Filter by `type`, `status`, `player_id`, `round_id` (either identifier) and a date window. Each row carries `wallet_round_id`, so movements join to rounds exactly rather than by timestamp and amount. ```bash curl "https://api.casinoapipro.com/v1/transactions?type=WIN&from=2026-09-01" \\ -H "Authorization: Bearer $ACCESS_TOKEN" ``` ## Sessions `GET /v1/sessions` — auth: operator-api Filter by `player_id`, `status` (`ACTIVE`, `EXPIRED`, `CLOSED`, `REVOKED`) and a window. Creating one is on [sessions and launch](/docs/sessions). ## Players `GET /v1/players` — auth: operator-api `GET /v1/players/{playerId}` — auth: operator-api Players we have seen on your account, addressed by your own id. `q` substring matches. For a player's money position rather than their record, see [the reporting API](/docs/reports). `player_name` is the display name we hold for them — whatever you last sent on [a session](/docs/sessions), or `null` if you have never sent one. It is here so you can read back what we store rather than infer it from what you sent. ## Free bets `GET /v1/freebets` — auth: operator-api Grants held on your account, with `remaining` against `quantity` and the `total_win` they have produced — which is the entire cost of a free bet to you, since no debit is ever raised. Filter by `player_id` and `status` (`ACTIVE`, `SPENT`, `EXPIRED`, `REVOKED`). `POST /v1/freebets/{grantId}/revoke` — auth: operator-api Withdraws whatever is left of a grant and answers with `unspent`, the number of bets that will now never be played. Only an `ACTIVE` grant can be revoked; one already spent, expired or revoked is refused rather than quietly accepted. > **Only the unplayed part** > > Free bets already spent have produced rounds, and a round that happened cannot be un-happened. Revoking is not a refund — it stops what has not been used. ## What is still dashboard-only, and why A few things are deliberately not reachable with an API key, because a key that can do them is a key worth stealing: - Issuing and revoking credentials. A key that can mint keys turns one leak into permanent access. - Webhook endpoints. No machine credential should be able to redirect where your events are delivered. - Wallet configuration. Same argument, with money on the other end of it. - Team, businesses and billing. These are about people and contracts rather than traffic. Everything else is either here already or a gap worth reporting. We would rather add an endpoint than have you drive a browser.