# Games API

## You call GA: launch, catalog, free rounds, reports

Base URL `https://api.rexplay.site` (sandbox) or `https://api.game-alligator.com` (production). Every call is `POST` with a JSON body and the signature of §2.3. Paths are `/v2/aggregator/<call>` in snake_case. The sandbox also accepts the proto method name (`/v2/aggregator/LaunchGame`, case-sensitive). Sign exactly the path you call. Success is `200` with the call's own response. Amounts are strings in minor units (§2.6). Timestamps are Request for Comments (RFC) 3339. Paginated calls take `page: {cursor, limit}` and return `page: {next_cursor, has_more, total_count}`. `limit` defaults to 50, with a maximum of 500.

Start from Postman: import `postman/GA_Aggregator_API_v2.postman_collection.json` with `postman/GA_Sandbox_v2.postman_environment.json`, set `ga_api_key_id` and `ga_hmac_secret`, and send `launch_game` first. The pack with both files is `/downloads/GA_Operator_Integration_Pack_v2.0.0.zip` on the docs site.

## Launch a game

`POST /v2/aggregator/launch_game`

| Field | Required | Meaning |
|---|---|---|
| `player_ref` | yes | Your player id. Opaque to GA, returned unchanged in every wallet call. |
| `game_id` | yes | From the catalog (§4.3). |
| `currency` | yes | The currency of this session. Uppercase code. GA takes the exponent from your onboarding profile. |
| `token` | yes | **Your launch token.** GA stores it and sends it back as `launch_token` in every wallet call of this session. It's how you tie a wallet call to a player session. |
| `return_url` | no | Where the game's "back to lobby" button leads. |
| `attributes` | no | String map. `attributes["language"]` = two-letter lowercase code (`"de"`). Anything else falls back to `"en"`. |

Response: `session_id`, `launch_url`, `expires_at` (the session lives **24 hours**), plus running `total_bet`, `total_win`, `rounds`.

`launch_game` is idempotent by `token` while the session is open. The same token with the same player, game, and currency returns the first response again: the same `session_id`, `launch_url`, and `expires_at`. So a page reload doesn't open a second session, and GA doesn't call the game provider again.

GA refuses the same token with `409` `ERROR_CODE_IDEMPOTENCY_CONFLICT` (`session_token_conflict`) in these cases:

- the token comes with another player, game, or currency;
- the session of that token is closed: by `close_session`, or by a newer launch of the same game for the same player, because a launch closes that player's earlier sessions of the game;
- the session of that token is past its `expires_at`.

GA never reopens a closed session. Don't retry the `409`: launch again with a fresh token. If the first launch with the token is still running, GA answers `503` `ERROR_CODE_INTERNAL` with `retryable: true` (`launch_in_progress`). Retry with the same token to get the first response. Use a fresh token for every new session.

Open `launch_url` in an iframe or by redirect. Both work. The URL is single-session: launch again for a new session. Device, IP, and country aren't fields on this call.

If you embed `launch_url` in an `iframe`, set a `sandbox` attribute. Don't omit it, and don't use an unrestricted frame. `launch_url` always points to a real address that you load via `src`, never as inline HTML, so the minimal working set is:

```html
<iframe
  src="https://play.rexplay.site/s/0198a1cb-4f37-7ee8-8e7d-08d39925aec0"
  sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
  allow="autoplay; fullscreen"
  allowfullscreen>
</iframe>
```

- `allow-scripts`—the game is a script-driven client. Without it, nothing runs.
- `allow-same-origin`—the frame loads from `src`, so this keeps it same-origin with *GA's* domain, not yours, which the game's own websocket connection and storage need. It doesn't weaken your page's origin boundary.
- `allow-forms`—some game and payment flows submit forms inside the frame.
- `allow-popups`—rule and payment/cashier windows some games open are new browser contexts, not just navigation inside the frame.
- `allow="autoplay; fullscreen"` and `allowfullscreen`—background/video-driven games and fullscreen mode need these. Playback or fullscreen silently fails without them.

```json
{
  "player_ref": "player-10428",
  "game_id": "0198a2aa-1111-7000-a000-000000000077",
  "currency": "EUR",
  "token": "lt-abcdef123456",
  "return_url": "https://casino.example/lobby",
  "attributes": {
    "language": "de"
  }
}
```
```json
{
  "session_id": "0198a1cb-4f37-7ee8-8e7d-08d39925aec0",
  "player_ref": "player-10428",
  "game_id": "0198a2aa-1111-7000-a000-000000000077",
  "currency": "EUR",
  "launch_url": "https://play.rexplay.site/s/0198a1cb-4f37-7ee8-8e7d-08d39925aec0",
  "expires_at": "2026-09-14T12:00:00Z",
  "total_bet": {
    "currency": "EUR",
    "amount": "0",
    "exponent": 2
  },
  "total_win": {
    "currency": "EUR",
    "amount": "0",
    "exponent": 2
  },
  "rounds": 0
}
```

## Launch a demo

`POST /v2/aggregator/launch_demo` with `game_id` and optional `return_url`. No player, no token, no currency, no session: a demo never reaches your wallet. Response: `launch_url`, `game_id`. Games with `demo_supported: false` refuse with `412` (`DEMO_NOT_SUPPORTED`, Appendix A.2).

## Catalog

`POST /v2/aggregator/games` with optional filters `query`, `vertical`, `category`, `provider`, `tags[]`, `updated_since`, `page`.

Each `Game` carries: `game_id`, `title`, `vertical`, `category`, `provider`, `enabled`, `demo_supported`, `wager_weight_bp`, `tags[]`, `assets[]`, `limits`, `fee_bp`, `fee_source`, `rtp`, `features[]`, `updated_at`. `wager_weight_bp` is how much a bet on this game contributes to bonus wagering and loyalty accrual, in basis points, 0 to 10000. 10000 bp = 100 %, 1000 bp = 10 %, 0 bp = 0 %. `assets[]` is `{kind, url}` with kinds `banner`, `thumbnail`, `background`, `icon`. `limits` is `min_bet`, `max_bet`, `max_win`. `fee_bp` is GA's fee for this game on your brand, in basis points, 0 to 10000. It's an `optional` field—present only once GA has resolved a fee for this game/brand pair, absent otherwise, so check presence rather than treating "absent" as 0. `fee_source` is which fee-ladder tier produced `fee_bp`: `FEE_SOURCE_UNSPECIFIED`, `FEE_SOURCE_PROVIDER`, `FEE_SOURCE_GAME`. `rtp` is the return to player (RTP) in percent, for example `96.5`. The wire value is also `0` when GA has recorded no RTP for this game, so a `0` doesn't mean "0 % RTP".

`features[]` is always empty in this call. Call `get_game_features` (§6.1) to get a game's actual `features[]`.

Practical rules:

- Poll with `updated_since` (the last `updated_at` you stored) rather than reloading the whole catalog. A daily full reload is enough.
- Download `assets[].url` images to your own content delivery network (CDN) and serve them from there. Don't hotlink them from your lobby.
- Show only `enabled: true` games. A disabled game refuses to launch.

Other catalog calls are `capabilities`, `operator_capabilities`, and `update_game`. `capabilities` is what GA supports for you: currencies, settlement modes, streaming. `operator_capabilities` is what GA has on record for your wallet: `wallet_capabilities` with `rpcs`, `credit_kinds`, `debit_modes`, `notify_kinds`, `authenticate`, and `supported_transports`. `update_game` changes your own settings on a game, partial update with `update_mask` and a `reason` for audit.

## Reference

- **POST** `/v2/aggregator/games` — [listGames: parameters, errors, code samples, try it](/docs/aggregation/reference/aggregator/#tag/aggregator/POST/v2/aggregator/games)
- **POST** `/v2/aggregator/launch_game` — [launchGame: parameters, errors, code samples, try it](/docs/aggregation/reference/aggregator/#tag/aggregator/POST/v2/aggregator/launch_game)
- **POST** `/v2/aggregator/launch_demo` — [launchDemo: parameters, errors, code samples, try it](/docs/aggregation/reference/aggregator/#tag/aggregator/POST/v2/aggregator/launch_demo)
