Integrate

API reference

Updated 13 October 2026

Download openapi.b2b.yaml OpenAPI 3.1 · version 2.1.0

Companion of the RGS integration guide. Two halves:

  • RGS side (servers below): POST /b2b/launch, the signed reads under /b2b/…, GET /games, GET /health and GET /ready, GET /fair and the public round verification GET /fair/{room}/{nonce} (JSON) / …/verifica (HTML).
  • Operator side: the four wallet callbacks (/bet, /win, /rollback, /balance) that the operator hosts under its walletBaseUrl, plus the optional events webhook (eventsWebhookUrl, tag events); they carry a path-level servers entry so tools keep them apart from ours.

Signature v2, one rule for every call in both directions. Each request carries x-timestamp (epoch milliseconds) and x-signature, the lowercase hex HMAC-SHA256, keyed with the operator's secret, of

NERVE-V2\nrequest\n<METHOD>\n<path>\n<timestamp>\n<body>

and every wallet response the operator sends is signed over

NERVE-V2\nresponse\n<METHOD>\n<path>\n<timestamp>\n<status>\n<sha256 hex of the request body>\n<body>

On calls to the operator the path is the logical one (/bet, /win, /rollback, /balance, /events), whatever prefix the configured URL carries; on calls to the RGS it is the request target exactly as sent, query string included. Every wallet response must be signed by the operator with direction response, the x-timestamp of the request it answers, the HTTP status it carries and the SHA-256 of the request body it answers (2.1) — an unsigned or mis-signed response, a rewritten status included, counts as no response. Requests are valid for 60 seconds. Redirects are never followed. The integration guide (§6.1) has sample code and test vectors.

Timeouts and retries on every wallet callback. The RGS waits walletTimeoutMs (default 4000 ms) per attempt, from the request to the last byte of the body. On a network error or a timeout it retries walletRetries times (default 1, so two attempts in total) with the very same body and betId; it never retries because of an HTTP response, whatever the status. So the same /bet, /win, /rollback or /balance body can legitimately arrive twice: the idempotency on betId is what makes that harmless. Both numbers are per operator, in the registry (see OperatorRegistryEntry under components/schemas, the reference for every registry field: allowedOrigins, enabled, ipAllowlist, limits, eventsWebhookUrl…).

Retries of calls to the RGS. A signature names one request: once verified, the RGS remembers it for two minutes. The same signed request sent again from the same IP address is a retry, not a new request: POST /b2b/launch and the promo opt-out answer it with the response they gave the first time — same status, headers and body, the same game token — plus idempotent-replayed: true, whatever that status was; a read is simply answered again. From a different IP address it is 401 REPLAY. A retry that arrives while the first request is still being processed waits for it, up to 15 seconds, then answers 409 REPLAY_IN_PROGRESS. A new attempt is a new signature, with a fresh x-timestamp.

Authentication

SchemeHeaderWhat is signed
launchSignaturex-signaturesignature v2 (see the introduction) with direction request, method POST, path /b2b/launch and the exact body; sent with x-timestamp, valid for 60 seconds
readSignaturex-signaturesignature v2 (see the introduction) with direction request, the method, the path with its query exactly as sent, and the body (empty on GET); sent with x-timestamp, valid for 60 seconds. Same secret as the launch and the wallet callbacks; the path is inside the signature, so a signature captured on one endpoint does not open another.
callbackSignaturex-signaturesignature v2 (see the introduction) with direction request, method POST, the logical path (/bet, /win, /rollback, /balance, /events) and the exact body; sent with x-timestamp. The operator verifies it on the raw bytes, with a constant-time comparison

Launch

opening the game for a player (RGS side)

POST /b2b/launch

Authorise a player and get the iframe URL

Called by the operator's backend. Signed with signature v2 (see the introduction): direction request, method POST, path /b2b/launch, and the body exactly as sent, byte for byte — serialise once, sign that string, send that string. x-timestamp must be within 60 seconds of the RGS clock.

Signed: launchSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds

Request body

LaunchRequest

FieldTypeRequiredDescription
operatorstringyesoperator id
playerPlayerIdyes
roomstring · pattern ^[a-zA-Z0-9_-]{1,64}$yesone of the operator's room ids (real-money or demo)
displayNamestring · max 64 charsthe public name other players see (cleaned to 24 chars); a generated alias otherwise
langstring · pattern ^[a-z]{2}(-[A-Z]{2})?$opens the client in this language (en, es; regional tags map to the base)
lobbyUrlstring · uri · max 512 charshttps URL of the lobby — “Back to the lobby”, and the only origin that receives balance bridge events
sessionIdstring · pattern ^[\x21-\x7e]{1,64}$your own session id for this player (optional): a different value starts a new responsible-gaming session (time, reality check, session cap, net); without it a 30-minute absence does. Not an identity: the wallet never sees it

Responses

StatusMeaningBody
200launch acceptedLaunchResponse application/json
400a field is missing or malformed (field names it: one of player, room, x-timestamp, displayName, lang, lobbyUrl, sessionId). A missing or non-string operator is not a 400: it is 404 OPERATOR_UNKNOWNLaunchError application/json
401STALE (x-timestamp outside the 60 s window), REPLAY (a signature already seen within the last two minutes, sent again from a different IP address; from the same address it is a retry, and gets the first answer) or BAD_SIGNATURELaunchError application/json
403OPERATOR_SUSPENDED (kill-switch, enabled: false in the registry), IP_NOT_ALLOWED (outside the operator's allowlist) — both checked only after a valid signature — or ROOM_NOT_OWNED: the room does not exist, is not this operator's, or is neither real-money nor demoLaunchError application/json
404OPERATOR_UNKNOWN: the operator id is not in the registry — or is missing from the body, or is not a string. Answered before the signature is checked: operator ids are not secretLaunchError application/json
409REPLAY_IN_PROGRESS: a retry of a launch that is still being processed, and did not finish within 15 seconds. Ask again in a moment: once the first finishes, a retry gets its answerLaunchError application/json
429RATE_LIMITED: this IP has already had 30 launches refused with 400, 401, 403 or 404 in the last minute. Successful launches never count, and neither do the 503 answers belowLaunchError application/json
503ROOM_INACTIVE (room paused), MAINTENANCE (a demo room during a maintenance window: no new launches, players inside finish; a real-money room answers RGS_NOT_READY with MAINTENANCE in issues), RGS_NOT_READY (+ issues, real-money room while the readiness checks of /ready fail, or any launch while the database is unreachable: DB_NOT_READY), ROOM_RECOVERY_REQUIRED (room held by a previous process after a crash; in a room with tables, no table can take a player and one is held), ROOM_FULL (a room with tables: every open table is at its cap and no new one can open yet; never to a player who already has a seat in the room), TOKEN_DISABLED, DB_REQUIREDLaunchError application/json

POST /b2b/players/{player}/sessions/revoke

End a player's sessions now

Ends every game session of the player at once (since 13 October 2026): the player's open sockets close with 4401 — the game shows that the session has ended and offers the way back to your lobby — and every game token issued to the player up to this instant, the launch token and its renewals alike, is refused from now on, at the handshake and on /me/*. A launch made after the call issues a fresh token that works. Use it when you close or block the account, when a self-exclusion lands, or when a gameUrl has been shared where it should not have been: before this endpoint a leaked link lived until its hour ran out.

Signed like the read endpoints (signature v2, method POST, the path with its query exactly as sent), sent with no body. The revocation lives in the memory of the game process (one replica per service): a restart forgets it, and a token issued before the restart would be accepted again within its own one-hour lifetime. That is the residual risk, stated on purpose.

Every accepted call writes an audit row on our side (action session.revoke, orgId = the calling operator, no admin actor). A retry of the same signed call, from the same IP address within two minutes, gets the first response with idempotent-replayed: true and writes nothing.

Signed: readSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
playerpathPlayerIdyes
operatorquerystringyes
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds

Responses

StatusMeaningBody
200the sessions are endedobject application/json
400BAD_REQUEST: player longer than 128 characters (message: "player too long"), not printable ASCII without spaces (message: "player: printable ASCII without spaces"), or missing/non-numeric x-timestamp
401STALE, REPLAY (a signature already seen within two minutes, from another IP address; from the same one the call is a retry) or BAD_SIGNATURE
403OPERATOR_SUSPENDED or IP_NOT_ALLOWED
404OPERATOR_UNKNOWN
409REPLAY_IN_PROGRESS: a retry of a call still being processed after 15 seconds; ask again in a moment
429RATE_LIMITED (failed calls only; the same per-IP budget as the reads)

Catalog

machine-readable game list and icons (RGS side)

GET /games

The game catalog, built from the engine constants

Responses

StatusMeaningBody
200all games with math, limits, features and absolute icon URLsCatalog application/json

GET /games/{id}

One catalog entry

Parameters

NameInTypeRequiredDescription
idpathstring · one of god-candle, diamond-hands, big-fish, hypnofrogyes

Responses

StatusMeaningBody
200the entryCatalogGame application/json
404GAME_UNKNOWN

GET /games/{id}/{file}

Game icon (SVG, or PNG at 512 / 256 / 128 px)

Parameters

NameInTypeRequiredDescription
idpathstring · one of god-candle, diamond-hands, big-fish, hypnofrogyes
filepathstring · one of icon.svg, icon-512.png, icon-256.png, icon-128.pngyes

Responses

StatusMeaningBody
200the icon, cacheable for a daystring image/svg+xml
string · binary image/png
404unknown game or file

Reads

signed read-only endpoints for the operator's own rounds, bets and settlement (RGS side)

GET /b2b/settlements

Durable wallet transactions, including interrupted rounds

Signed with signature v2 (method GET, the path with its query exactly as sent, empty body). Both ISO bounds are required. Selects stakes by the moment the debit was requested, in [from, to), at most 90 days. Contains OPEN and ROLLBACK_PENDING as well as terminal stakes. Decimal amounts are strings. A null payout means no recorded win obligation, not zero. The delivery status is separate from the stake status. No cross-currency total. truncated and x-report-truncated require splitting the time window when true. CSV protects formula-like identifiers with a leading apostrophe; JSON preserves exact IDs.

Signed: readSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
operatorquerystringyes
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds
fromquerystring · date-timeyesincluded
toquerystring · date-timeyesexcluded
formatquerystring · one of json, csv

Responses

StatusMeaningBody
200Scoped ledger rows in JSON or spreadsheet-safe CSV, capped at 20000 rows. The CSV (content-disposition: attachment; filename="wallet-settlements.csv", UTF-8 with BOM) has the same columns as the JSON rows, in this order: betId, playerId, roomId, roundId, currency, stake, stakeStatus, payout, deliveryStatus, multiplier, createdAt, updatedAt, deliveryUpdatedAt. SettlementsResponse application/json
string text/csv
400BAD_RANGE (from/to missing, not ISO dates, or from ≥ to), RANGE_TOO_WIDE (more than 90 days), or BAD_REQUEST (missing or non-numeric x-timestamp)
401STALE
403OPERATOR_SUSPENDED or IP_NOT_ALLOWED
404OPERATOR_UNKNOWN
429RATE_LIMITED
503DB_REQUIRED (no database on this service) or DB_UNAVAILABLE (database configured but unreachable right now: retry)

GET /b2b/round/{id}

One round, as its operator sees it

id is the roundId we send you on every /bet and /win, i.e. <table>:<nonce>: the table is the room id itself unless the room has tables, then <room>--t<n> from table 2.

Signed with signature v2, with the same secret as the launch: method GET, the path with its query exactly as sent (percent-encoded as sent: rm-acme%3A42 and rm-acme:42 are two different signed strings), empty body. The path is inside the signature, so a signature captured on one endpoint does not open another.

Returns the outcome, the generator inputs (so the round can be re-derived — see also the public /fair/{roomId}/{nonce}), the round totals, and the caller's own bets. A round appears only once it has settled: while it is running the answer is 404 NOT_FOUND. bets[].betId is the wire betId — the one you saw on /bet and /win, and the one /b2b/bet/{id} accepts — never an internal identifier.

Signed: readSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
idpathstringyes
operatorquerystringyes
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds

Responses

StatusMeaningBody
200the roundOperatorRound application/json
400BAD_ROUND_ID (not <roomId>:<nonce>) or BAD_REQUEST (missing/non-numeric x-timestamp)
401STALE, REPLAY (a signature already seen within two minutes, from another IP address; from the same one the call is a retry) or BAD_SIGNATURE
403OPERATOR_SUSPENDED or IP_NOT_ALLOWED (after a valid signature, as on the launch)
404OPERATOR_UNKNOWN, or NOT_FOUND. NOT_FOUND is returned when the round does not exist, when it has not settled yet, and when it belongs to another operator, on purpose: a different answer would let a caller with a valid secret probe another operator's rounds.
429RATE_LIMITED (failed reads only)
503DB_REQUIRED (no database on this service) or DB_UNAVAILABLE (database configured but unreachable right now: retry)

GET /b2b/bet/{id}

One bet, and why the player has not been paid

id is the betId you saw on /bet. Signed with signature v2 (method GET, the path with its query exactly as sent, empty body).

⚠️ betIds contain # (<room>.<instance>:<nonce>:<slot>#<n>): percent-encode the id in the path (%23), or your HTTP client cuts the URL at the #, the ?operator= query with it, and the answer is 404 OPERATOR_UNKNOWN. Sign the encoded path, exactly as sent.

Returns the stake's state towards your wallet (OPEN debit requested and position not closed yet, an ambiguous /bet included · REJECTED refused by you with a signed 4xx, never debited · SETTLED outcome final on our side, won or lost — the delivery of the credit is win.status · ROLLBACK_PENDING refund claimed by our sweeper · ROLLED_BACK refunded), the state of the win obligation, and the timeline of what the server received for that bet, in arrival order.

Signed: readSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
idpathstringyes
operatorquerystringyes
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds

Responses

StatusMeaningBody
200the betOperatorBet application/json
400BAD_BET_ID (longer than 256 characters) or BAD_REQUEST (missing/non-numeric x-timestamp)
401STALE, REPLAY (a signature already seen within two minutes, from another IP address; from the same one the call is a retry) or BAD_SIGNATURE
403OPERATOR_SUSPENDED or IP_NOT_ALLOWED
404OPERATOR_UNKNOWN, or NOT_FOUND (unknown or not yours — see /b2b/round/{id})
429RATE_LIMITED
503DB_REQUIRED (no database on this service) or DB_UNAVAILABLE (database configured but unreachable right now: retry)

GET /b2b/report

Settlement per currency and day

Signed with signature v2 (method GET, the path with its query exactly as sent, empty body): the window is inside the signature, so a signature for one day does not open the whole history. Both bounds are required, exactly as on /b2b/settlements (400 BAD_RANGE "from/to required" without them); from is included and to excluded, so September is from=2026-09-01&to=2026-10-01. There is no other window parameter.

Rows are (currency, day of settlement) — keys currency, day — with the number of bets (bet), distinct players (player), wagered, payout and ggr, plus per-currency subtotals (subtotals, which carry no player count: distinct players do not add up across days). See ReportResponse. There is never a grand total across currencies: adding euros and dollars produces a number that exists nowhere.

Signed: readSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
fromquerystring · dateyesincluded
toquerystring · dateyesexcluded
formatquerystring · one of csvomit for JSON
operatorquerystringyes
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds

Responses

StatusMeaningBody
200rows, subtotals, and whether the result was truncated (truncated: the row cap, 20000 by default, was reached — narrow the window). With format=csv: content-disposition: attachment; filename="settlement-<operator>-<from>_<to>.csv", UTF-8 with BOM, columns currency, day, bet, player, wagered, payout, ggr; the CSV carries no subtotals. ReportResponse application/json
string text/csv
400BAD_RANGE (from/to missing, not ISO dates, or from ≥ to), RANGE_TOO_WIDE (max 90 days) or BAD_REQUEST (missing/non-numeric x-timestamp)
401STALE, REPLAY (a signature already seen within two minutes, from another IP address; from the same one the call is a retry) or BAD_SIGNATURE
403OPERATOR_SUSPENDED or IP_NOT_ALLOWED
404OPERATOR_UNKNOWN
429RATE_LIMITED
503DB_REQUIRED (no database on this service) or DB_UNAVAILABLE (database configured but unreachable right now: retry)

Status

health, readiness and provably-fair information (RGS side)

GET /b2b/room/{id}/status

Operational readiness of the caller's room without waking it

Signed with signature v2 (method GET, the path with its query exactly as sent, empty body). Timestamp window is 60 seconds. Does not wake the room. realMoney.ready is operational eligibility, not regulatory approval. tables lists the room's tables (a room without tables has one, itself); tables that finished their hash chain are counted in retiredTables and listed on /fair.

Signed: readSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
idpathstringyes
operatorquerystringyes
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds

Responses

StatusMeaningBody
200Room statusobject application/json
400BAD_REQUEST (missing or non-numeric x-timestamp)
401STALE
403OPERATOR_SUSPENDED or IP_NOT_ALLOWED
404OPERATOR_UNKNOWN, or NOT_FOUND for both a missing room and another operator's room
429RATE_LIMITED
503DB_REQUIRED (no database on this service) or DB_UNAVAILABLE (database configured but unreachable right now: retry)

GET /health

Liveness and identity of the installation

Does not touch the database unless ?db=1 is passed.

Parameters

NameInTypeRequiredDescription
dbquerystring · one of 1also measure database latency (dbMs)

Responses

StatusMeaningBody
200aliveHealth application/json

GET /ready

Readiness — can the installation take real-money traffic now

Unlike /health (liveness, always 200 while the process is alive) this answers 503 when the service should not receive traffic: database configured but never up at boot (dbBootOk: false) or fallen after it (db: ko), maintenance window, draining after a stop signal, or — on a casino installation — any failed real-money check (schema not migrated, generator fingerprint not verified). Zero database queries, like /health. Before sending real money, read realMoney.ready, not only the status: on an installation without a database (demo rooms only) the service is ready while realMoney.ready is false.

Responses

StatusMeaningBody
200readyReady application/json
503not ready (ready: false; realMoney.issues and maintenance/draining say why)Ready application/json

GET /fair

Provably-fair commitment of a room

Parameters

NameInTypeRequiredDescription
roomquerystring · default demoa room id, or a table id (<room>--t<n>): a table answers with its own fields and no tables list
tablesAfterqueryinteger · min 0 · default 0the cursor of the tables list: the last table number already read (tablesNext of the previous page)

Responses

StatusMeaningBody
200the chain terminus, client seed, generator version, current nonce and persistent of the room's first table — persistent is true when the room persists its rounds, so /fair/{roomId}/{nonce} and its /verifica page answer; false for in-memory demo rooms, which have no tables. Then tables, the room's tables in number order, 50 per page, and tablesNext. ⚠️ An unknown room also answers 200, with the body {error: ROOM_NOT_FOUND}: check for error before reading the fieldsFairInfo application/json
400BAD_REQUEST: tablesAfter is not a table number
429RATE_LIMITED: the public reads (/fair, /fair/{room}/{nonce}, its /verifica page, /meta/…) share one per-IP budget, 120 per minute by default

GET /fair/{room}/{nonce}

One settled round, with every generator input, to re-derive it independently

Public, like /fair: the seed of a settled round has already been revealed to every player. Does not wake the room. A Round row exists only after settlement, so a round still in progress is 404 ROUND_NOT_FOUND; so is any round of an in-memory demo room (persistent: false in /fair). Verification in three steps: SHA256(seed) === seedHash, SHA256^nonce(seed) === chainTerminus, and generateRound(seed, clientSeed, nonce, math) reproduces outcome. math is null for rounds written before the configuration snapshot existed.

Parameters

NameInTypeRequiredDescription
roompathstringyes
noncepathinteger · min 0yes

Responses

StatusMeaningBody
200the round's inputs and outcomeFairRound application/json
400BAD_REQUEST: bad room id or nonce
404ROUND_NOT_FOUND
429RATE_LIMITED: same per-IP budget as /fair (120 per minute by default)
503DB_DISABLED (this installation keeps no rounds) or DB_UNAVAILABLE (the database is unreachable right now: retry)

GET /fair/{room}/{nonce}/verifica

The same verification as an HTML page, for people

Runs the three checks server-side and shows the verdict; cacheable for an hour (a settled round never changes), and for 15 seconds while the round closed less than two minutes ago, since its command ledger may still be landing. Errors are HTML pages with the same status codes as the JSON endpoint — except the 429 of the per-IP limiter, which is the same JSON body as on /fair.

Parameters

NameInTypeRequiredDescription
roompathstringyes
noncepathinteger · min 0yes

Responses

StatusMeaningBody
200the verification pagestring text/html
400bad room id or nonce (HTML)
404round not found
429{error: RATE_LIMITED} as JSON, not HTML: same per-IP budget as /fair
503no database on this installation

Wallet

seamless-wallet callbacks implemented by the OPERATOR under its walletBaseUrl

POST /bet you implement

Debit a stake (operator-implemented)

Idempotent on betId, also under concurrency. Signed both ways (signature v2, logical path /bet). A 200 with a finite, non-negative balance means debited; a signed 4xx is a definitive refusal (nothing debited, the player sees reason when it is one of the table, ERROR otherwise); everything else is ambiguous and triggers an immediate /rollback from the RGS: 5xx, any other status below 400 including redirects (never followed), a timeout, an unsigned or mis-signed response, and a signed 200 whose balance is missing, not a number, or negative (the RGS does not know what you did with the stake, and will not show the player a number that does not exist). A /bet arriving after a /rollback of the same betId must answer 409 {reason: ROLLED_BACK}. Because of the network retry (see the introduction) the same /bet body can arrive twice: answer the second exactly like the first.

Signed: callbackSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
x-operatorheaderstringyesthe operator id the RGS is calling on behalf of

Request body

WalletMoveRequest

FieldTypeRequiredDescription
playerIdPlayerIdyes
betIdBetIdyes
amountMoneyyes
currencyCurrencyyes
roundIdRoundIdyes
multipliernumber · min 0Only on /win: the multiplier the position closed at, with four decimals and the same value on every retry (relative to entry: 1.0 is break-even before the house edge; God Candle can close below 1.0), or 0 on a lost bet (amount: 0). Absent on /bet. Optional: ignore it if you do not need it. It is what a tournament tool needs to rank by "max multiplier" instead of "total staked". The payout is not always stake × multiplier: a position opened mid-round in God Candle, Diamond Hands or Hypnofrog is paid stake × multiplier × RTP (guide §6.2).

Responses

StatusMeaningBody
200debitedWalletOk application/json
402refused — INSUFFICIENT_FUNDS (any signed 4xx with a reason from the table is read the same way)WalletReject application/json
409ROLLED_BACK: this betId was rolled back before the debit arrived; never debitedWalletReject application/json

POST /win you implement

Credit a payout, or close a lost bet with amount 0 (operator-implemented)

Idempotent on betId, also under concurrency. amount: 0 closes a LOST bet and must be accepted like any win; after it, a /rollback of the same betId must answer 409 ALREADY_SETTLED. Anything but a signed 200 is retried by the RGS with backoff for about 26 hours. At the end of a round every open position is closed at once: expect up to 32 /win calls in flight from one RGS server. Never credit a /win for a betId you did not debit, or rolled back: by design we never send one, and if one arrives answer it with a signed 409 and alert your team — it is retried, then settled by hand. amount is stake × multiplier × f, floored and capped at the room's maxWin: f is the room's RTP for a position opened mid-round in God Candle, Diamond Hands or Hypnofrog, 1 otherwise.

Signed: callbackSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
x-operatorheaderstringyesthe operator id the RGS is calling on behalf of

Request body

WalletMoveRequest

FieldTypeRequiredDescription
playerIdPlayerIdyes
betIdBetIdyes
amountMoneyyes
currencyCurrencyyes
roundIdRoundIdyes
multipliernumber · min 0Only on /win: the multiplier the position closed at, with four decimals and the same value on every retry (relative to entry: 1.0 is break-even before the house edge; God Candle can close below 1.0), or 0 on a lost bet (amount: 0). Absent on /bet. Optional: ignore it if you do not need it. It is what a tournament tool needs to rank by "max multiplier" instead of "total staked". The payout is not always stake × multiplier: a position opened mid-round in God Candle, Diamond Hands or Hypnofrog is paid stake × multiplier × RTP (guide §6.2).

Responses

StatusMeaningBody
200credited (or closed, for amount 0)WalletOk application/json

POST /rollback you implement

Refund a stake (operator-implemented)

Idempotent on betId. Refund the stake of an open bet. A bet already closed with /win (including amount 0) → 409 ALREADY_SETTLED, no refund. A betId never seen → 200 no-op, and REMEMBER it: a later /bet with that id must be refused with 409 ROLLED_BACK. The request carries no player, so answer { "balance": 0 }: the balance of a /rollback answer is never used for accounting, and a 0 is never shown to the player.

Signed: callbackSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
x-operatorheaderstringyesthe operator id the RGS is calling on behalf of

Request body

FieldTypeRequiredDescription
betIdBetIdyes

Responses

StatusMeaningBody
200refunded, or nothing to refund (unknown or already rolled back)WalletOk application/json
409ALREADY_SETTLED: the bet was closed with /win; not refundedWalletReject application/json

POST /balance you implement

Current balance of a player (operator-implemented)

Signed: callbackSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
x-operatorheaderstringyesthe operator id the RGS is calling on behalf of

Request body

FieldTypeRequiredDescription
playerIdPlayerIdyes
currencyCurrencyyes

Responses

StatusMeaningBody
200the balance (anything else is shown to the player as 0)WalletOk application/json

Promo

promotions and player exclusions (operator-signed writes; operator free bets follow on this surface)

POST /b2b/promo/players/{player}/opt-out

Exclude a player from every promotion (or re-admit them)

Sets the player's promo opt-out flag. A player with the flag set is offered no promotion and cannot claim any: today that covers the welcome free bets of the operator's demo rooms, and every promotion that reaches real money will obey it. Use it to mirror your own harm indicators (UKGC SR Code 3.4.3, requirement 10). The player row is created if the player never launched a game: the exclusion usually precedes the first session, and that is the case that must work.

Signed like the read endpoints (signature v2, method POST, the path with its query exactly as sent), sent with no body. optOut is in the signed query: a signature for "exclude" is not valid for "re-admit".

Every accepted call writes an audit row on our side (action promo.opt_out, the flag before and after, orgId = the calling operator, no admin actor): the exclusion is a compliance decision, and who took it stays written. A retry of the same signed call, from the same IP address within two minutes, gets the first response with idempotent-replayed: true and writes nothing.

Signed: readSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
playerpathPlayerIdyes
operatorquerystringyes
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds
optOutquerystring · one of 0, 1yes1 excludes, 0 re-admits

Responses

StatusMeaningBody
200current stateobject application/json
400BAD_REQUEST: optOut not 0/1 (message: "optOut must be 0 or 1"), player longer than 128 characters (message: "player too long") or not printable ASCII without spaces (message: "player: printable ASCII without spaces"), or missing/non-numeric x-timestamp. The signature is checked first: a bad signature on a bad optOut is a 401
401STALE, REPLAY (a signature already seen within two minutes, from another IP address; from the same one the call is a retry) or BAD_SIGNATURE
403OPERATOR_SUSPENDED or IP_NOT_ALLOWED
404OPERATOR_UNKNOWN
409REPLAY_IN_PROGRESS: a retry of a call still being processed after 15 seconds; ask again in a moment
429RATE_LIMITED (failed calls only; the same per-IP budget as the reads)
503DB_REQUIRED (no database on this service) or DB_UNAVAILABLE (database configured but unreachable right now: retry)

Events

game events that are not transactions, POSTed by the RGS to the OPERATOR's eventsWebhookUrl (optional, no retry)

POST {eventsWebhookUrl} you implement

Receive a game event that is not a transaction (operator-implemented, optional)

Sent only when the registry entry has an eventsWebhookUrl (https, or http on loopback for local tests); without it nothing is sent. Signed like the wallet callbacks — signature v2, direction request, method POST, logical path /events whatever the configured URL is: the same code that verifies /bet verifies this. x-nerve-event repeats the event name so a router can dispatch without parsing the body; x-operator names the operator.

Timeout walletTimeoutMs (default 4000 ms), no retry and no queue: a timeout or any non-2xx is logged and dropped. It is a signal to your CRM, not an obligation to reconcile, and a signal delivered ten minutes late is worthless. The body carries no amounts, losses or balance — only the event, the room, the player as you know them, a count and the instant. The response body is ignored and need not be signed.

Events today: onboarding_at_risk — a player with no first bet on record has closed N bets (default 5) in this session without a single cashout. Emitted at most once per session.

Signed: callbackSignature (see Authentication)

Parameters

NameInTypeRequiredDescription
x-operatorheaderstringyesthe operator id the RGS is calling on behalf of
x-timestampheaderstring · pattern ^[0-9]{1,16}$yesepoch milliseconds, digits only; signed, and valid for 60 seconds
x-nerve-eventheaderstring · one of onboarding_at_riskyesthe event name, identical to event in the body

Request body

OperatorEvent

FieldTypeRequiredDescription
eventstring · one of onboarding_at_riskyes
roomIdstringyesthe room you launched, never a table id
playerstring · max 128 charsyesthe player id exactly as you sent it in the launch — never our internal id
betsintegeryesclosed bets without a cashout in this session
atstring · date-timeyesISO 8601

Responses

StatusMeaningBody
200acknowledged (any 2xx counts; the body is ignored)

Schemas

PlayerId

the operator's pseudonymous player id, exactly as sent in the launch: printable ASCII without spaces, 1 to 128 characters (since 13 October 2026 a launch or opt-out with another character is a 400 on player)

string · pattern ^[\x21-\x7e]{1,128}$ · max 128 chars

Currency

ISO-style code configured for the operator (one per operator)

string

BetId

globally unique, table id first — <table>.<instance>:<nonce>:<slot>#<n>, where the table is the room id unless the room has tables and slot is a per-round public label, never the player id; opaque — idempotency key of bet, win and rollback

string · max 256 chars

RoundId

<table>:<nonce>, identical on every call of the round; the table is the room id unless the room has tables, then <room>--t<n> from table 2

string

Money

amount in major units of the operator's currency (12.5 is €12.50), with at most that currency's decimals: two for EUR or USD, none for JPY. Stakes are rounded and payouts floored to that scale

number

LaunchRequest

signed as the exact bytes sent; the timestamp travels in the x-timestamp header, not in the body

FieldTypeRequiredDescription
operatorstringyesoperator id
playerPlayerIdyes
roomstring · pattern ^[a-zA-Z0-9_-]{1,64}$yesone of the operator's room ids (real-money or demo)
displayNamestring · max 64 charsthe public name other players see (cleaned to 24 chars); a generated alias otherwise
langstring · pattern ^[a-z]{2}(-[A-Z]{2})?$opens the client in this language (en, es; regional tags map to the base)
lobbyUrlstring · uri · max 512 charshttps URL of the lobby — “Back to the lobby”, and the only origin that receives balance bridge events
sessionIdstring · pattern ^[\x21-\x7e]{1,64}$your own session id for this player (optional): a different value starts a new responsible-gaming session (time, reality check, session cap, net); without it a 30-minute absence does. Not an identity: the wallet never sees it

LaunchResponse

FieldTypeRequiredDescription
tokenstringyesthe game token (also in the gameUrl fragment)
gameUrlstring · uriyesthe iframe URL: <base>/<gc|dh|bf|hf>/?room=…[&lang=…]#t=<token> — use it as is; lobbyUrl travels inside the signed token
ttlSecintegeryes
aliasstringyesthe public name that will be shown
realMoneybooleanyesfalse when the room is the operator's demo room

LaunchError

FieldTypeRequiredDescription
errorstring · one of BAD_REQUEST, STALE, REPLAY, REPLAY_IN_PROGRESS, BAD_SIGNATURE, OPERATOR_SUSPENDED, IP_NOT_ALLOWED, ROOM_NOT_OWNED, OPERATOR_UNKNOWN, RATE_LIMITED, ROOM_INACTIVE, MAINTENANCE, TOKEN_DISABLED, DB_REQUIRED, RGS_NOT_READY, ROOM_RECOVERY_REQUIRED, ROOM_FULLyes
fieldstringthe offending field, on BAD_REQUEST
issuesarray of stringon RGS_NOT_READY: which readiness checks fail (MAINTENANCE, DRAINING, DB_NOT_READY, MIGRATIONS_NOT_VERIFIED, ENGINE_NOT_VERIFIED)

Catalog

FieldTypeRequiredDescription
providerstringyes
envstring · one of staging, prod
revstring · nullablegit revision of the running build
integrationobject
integration.launchstring
integration.walletstring
integration.healthstring
gamesarray of <a href="#schema-cataloggame">CatalogGame</a>yes

CatalogGame

FieldTypeRequiredDescription
idstring · one of god-candle, diamond-hands, big-fish, hypnofrogyes
gameTypestring · one of GOD_CANDLE, DIAMOND_HANDS, BIG_FISH, HYPNOFROGyes
namestringyes
taglinestring
categorystring · one of crashyes
statusstring · one of live, previewyes
pathstring · one of /gc/, /dh/, /bf/, /hf/yes
demoRoomstringyes
demoUrlstring · uriyes
fairUrlstring · uriyes
mathCatalogMathyes
limitsobjectyes
limits.minBetnumber
limits.maxBetnumber
limits.maxWinnumber · nullable
limits.positionsPerRoundintegerpositions a player can hold open at the same time; more can be opened during the round as earlier ones close
limits.configurablestring · one of per-room
currencystring · one of operatoryes
languagesarray of stringyes
platformsarray of stringyes
featuresarray of stringyes
fairnessobjectyes
fairness.modelstring · one of sha256-hash-chain
fairness.verifystring
iconsobjectyes
icons.svgstring · uri
icons.png512string · uri
icons.png256string · uri
icons.png128string · uri

CatalogMath

FieldTypeRequiredDescription
rtpnumber
houseEdgenumber
rtpConfigurableboolean
maxMultipliernumberHighest multiplier a round can actually reach. Equals capMultiplier only where the barrier is exact (God Candle); Diamond Hands, Big Fish and Hypnofrog stop below it (pre-cap rule).
capMultipliernumberThe configured cap.
generatorVersionstring
volatilitystring · one of high
medianRoundSecnumber
maxRoundSecnumber
p50MaxMultipliernumber
p90MaxMultipliernumber
reach2xnumber · nullable
reach10xnumber · nullable
simulatedRoundsintegerrounds simulated for the volatility figures; Hypnofrog's median round duration was measured over 300,000

Health

FieldTypeRequiredDescription
okbooleanalways true while the process is alive — liveness
worldstring · one of casino
envstring · one of staging, prod
activeRoomsinteger
dbstring · one of absent, ready, down, koabsent = no database configured; down = not reachable at boot; ko = the last real access failed after boot; ready = up at boot and no failure since. Zero queries: observed, not probed
dbLastOkSecinteger · nullableseconds since the last successful database access
migrationsstring · nullableok, behind:N (N migrations not applied — the only way to see a casino database left behind, since those services never run migrate deploy), ahead:N, or null when unknown
improntastring · one of ok, diversa · nullableoutcome of the last self-check of the generators of this process: ok, diversa (a critical alarm is open), null = not checked yet
dbMsinteger · nullabledatabase round trip, only with ?db=1 (-1 on failure)
revstring · nullablegit revision of the running build

Ready

FieldTypeRequiredDescription
readyboolean
realMoneyobject
realMoney.readyboolean
realMoney.issuesarray of string · one of <code>MAINTENANCE</code>, <code>DRAINING</code>, <code>DB_NOT_READY</code>, <code>MIGRATIONS_NOT_VERIFIED</code>, <code>ENGINE_NOT_VERIFIED</code>
worldstring · one of casino
envstring · one of staging, prod
revstring · nullable
dbstring · one of absent, ok, ko, idle, unknownabsent (no database configured), or the observed state of the last real access: ok, ko, idle (pool closed after a quiet period — not a fault), unknown (nothing observed yet)
dbBootOkboolean · nullablenull when no database is configured
maintenancestring · nullablethe maintenance reason
drainingboolean
uptimeSecinteger
activeRoomsinteger

FairInfo

FieldTypeRequiredDescription
chainTerminusstringpublic end of the room's SHA-256 chain (the commitment); '' when the room row carries no terminus
clientSeedstring
generatorVersionstring
nonceintegercurrent round counter
persistentbooleantrue = the room persists its rounds, so /fair/{room}/{nonce} and its /verifica page answer; false for in-memory demo rooms
tablesarray of <a href="#schema-fairtable">FairTable</a>every table of the room in number order, retired ones included, 50 per page; a room without tables lists one, itself. Tables are created before they are needed, with their chain end published, and used in creation order: createdAt shows that a table existed before the rounds it played
tablesNextinteger · nullablethe value of tablesAfter for the next page; null on the last

FairTable

FieldTypeRequiredDescription
idstringyesthe room id for table 1, <room>--t<n> from table 2
numberintegeryes
chainTerminusstringyespublic end of this table's SHA-256 chain
clientSeedstringyes
createdAtstring · date-timeyes
retiredAtstring · date-time · nullableyeswhen the table finished its chain; null while it plays or can play

TableStatus

FieldTypeRequiredDescription
idstringyesthe room id for table 1, <room>--t<n> from table 2
numberintegeryes
activebooleanyesrunning in memory right now
usablebooleanyeswould take a player now, by the same rule as the launch
closedbooleanyestaken out of the seating by us: it empties and goes to sleep
retiringbooleanyesin the last rounds of its hash chain: takes no new players, then retires
recoveryRequiredbooleanyesheld by a previous process after a crash
quarantineUntilnumber · nullableyesend of a quarantine after a fault, ms since epoch

FairRound

FieldTypeRequiredDescription
roomIdstringthe table of the round: the room id, or <room>--t<n> in a room with tables
nonceinteger
gameTypestring · one of GOD_CANDLE, DIAMOND_HANDS, BIG_FISH, HYPNOFROG · nullable
clientSeedstring · nullablethe client seed of THAT round when stored, else the room's current one. A fixed text, or a drand round: drand:quicknet:<round>:<signature>, whose signature verifies against the quicknet public key (see the Provably fair page)
clientSeedFromRoundbooleantrue when clientSeed is the round's own
seedstringthe revealed server seed
seedHashstringSHA-256 of seed, committed before betting opened
chainTerminusstring · nullable
generatorVersionstring · nullable
mathobject · nullablethe generator configuration the round ran on; null for rounds older than the configuration snapshot
outcomeobject
outcome.endReasonstring · nullable
outcome.instantRugboolean · nullable
outcome.endPricestringa decimal string, exact as stored (a JSON number on /b2b/round)
outcome.maxMultiplierstringa decimal string, exact as stored (a JSON number on /b2b/round)
outcome.patternLabelstring · nullable
outcome.liveStartedAtstring · date-time · nullable
outcome.epochMsinteger · nullable
outcome.endedAtstring · date-time · nullable
positionsCompletebooleanfalse when the public list is truncated; Big Fish aggregate verification is then incomplete, and Hypnofrog positions beyond the cap are not checked
positionsarray of objectthe round's positions (at most 500), by betId — the one the operator's wallet saw on /bet, which starts with the room id — with no player identity
positions[].betIdstring
positions[].entryEpochinteger · nullable
positions[].fishIndexinteger · nullableBig Fish (fish index) and Hypnofrog (the position's initial fly)
positions[].switchesarray of object · nullableHypnofrog only — the fly switches accepted while the round was live, in order of receipt, from the command ledger; null when the ledger for that position is missing (the position is then not re-derivable)
positions[].switches[].beatintegerthe beat at which the switch reached the server
positions[].switches[].tointegerthe fly the position moved to
positions[].exitobject · nullableGod Candle and Diamond Hands only — the exit as the server priced it, read from the command ledger; null when the ledger has no row for the position (bots are not logged, so in a demo room this is the ordinary case, and the verifier then re-derives from the path alone). Why the position exited (take profit, stop loss, manual, rug) is not public
positions[].exit.epochintegerthe epoch (God Candle) or stab (Diamond Hands) at which the server priced the exit
positions[].exit.pricenumberthe absolute price the server applied at that epoch
positions[].exit.cashedOutbooleanfalse = the ledger closed the position with the rug (RUG_SETTLE)
positions[].exit.msAfterLaunchinteger · nullablethe command's receive time minus the launch
positions[].exit.amountnumber · nullablethe stake as the ledger recorded it
positions[].exit.multipliernumber · nullablethe closing multiplier as the ledger recorded it (four decimals)
positions[].exit.payoutnumber · nullablethe payout as the ledger recorded it
positions[].midRoundboolean · nullableGod Candle, Diamond Hands and Hypnofrog — true when the stake reached the server after the launch, so the mid-round slippage applies for certain (in Hypnofrog this is what tells a first-beat entry apart from a launch entry, both persisted with entry beat 0); false asserts nothing (a stake received before the launch can still be accepted after it while the wallet queue is busy); null when the ledger cannot tell
positions[].statusstring
positions[].cashoutMultipliernumber · nullable
positions[].amountnumber · nullablethe stake
positions[].payoutnumber · nullablethe payout
positions[].currencystring · nullable
verificationPagestringthe path of the HTML verification page, /fair/<room>/<nonce>/verifica

WalletMoveRequest

FieldTypeRequiredDescription
playerIdPlayerIdyes
betIdBetIdyes
amountMoneyyes
currencyCurrencyyes
roundIdRoundIdyes
multipliernumber · min 0Only on /win: the multiplier the position closed at, with four decimals and the same value on every retry (relative to entry: 1.0 is break-even before the house edge; God Candle can close below 1.0), or 0 on a lost bet (amount: 0). Absent on /bet. Optional: ignore it if you do not need it. It is what a tournament tool needs to rank by "max multiplier" instead of "total staked". The payout is not always stake × multiplier: a position opened mid-round in God Candle, Diamond Hands or Hypnofrog is paid stake × multiplier × RTP (guide §6.2).

WalletOk

FieldTypeRequiredDescription
balanceMoneyyes

WalletReject

FieldTypeRequiredDescription
reasonstring · one of INSUFFICIENT_FUNDS, PLAYER_UNKNOWN, PLAYER_BLOCKED, LIMIT_EXCEEDED, SESSION_EXPIRED, MAINTENANCE, ROLLED_BACK, ALREADY_SETTLEDyeson /bet, one of the translated reasons or ROLLED_BACK; on /rollback, ALREADY_SETTLED
balanceMoney

SettlementsResponse

FieldTypeRequiredDescription
operatorIdstringyes
fromstring · date-timeyesthe lower bound actually used (inclusive)
tostring · date-timeyesthe upper bound actually used (exclusive)
rowsarray of <a href="#schema-settlementrow">SettlementRow</a>yesordered by createdAt, then by insertion
truncatedbooleanyestrue when more rows exist than the cap (20000 by default): split the time window

SettlementRow

one stake as your wallet saw it, joined with its win obligation when one was recorded. Decimals are strings, exact

FieldTypeRequiredDescription
betIdBetIdyes
playerIdPlayerIdyes
roomIdstringyesthe table the stake was played at: the room id, or <room>--t<n> in a room with tables
roundIdRoundIdyes
currencyCurrencyyes
stakestringyesthe debited amount
stakeStatusstring · one of OPEN, REJECTED, SETTLED, ROLLBACK_PENDING, ROLLED_BACKyessame meaning as status on /b2b/bet/{id}
payoutstring · nullableyesthe win obligation; null = no recorded win obligation, not zero (a lost bet closed with /win amount 0 has a payout of zero, not null)
deliveryStatusstring · nullableyesPENDING (still retrying), SETTLED (your /win answered 200) or FAILED (no more automatic retries); null when there is no obligation
multiplierstring · nullableyesclosing multiplier (0 = lost); null when there is no obligation
createdAtstring · date-timeyeswhen the stake was requested from your wallet
updatedAtstring · date-timeyes
deliveryUpdatedAtstring · date-time · nullableyeslast change of the obligation

OperatorRound

FieldTypeRequiredDescription
roundIdRoundIdyes
roomIdstringyesthe table of the round: the room id, or <room>--t<n> in a room with tables
nonceintegeryes
gameTypestring · one of GOD_CANDLE, DIAMOND_HANDS, BIG_FISH, HYPNOFROGyes
endedAtstring · date-timeyes
outcomeobjectyesthe outcome
outcome.endReasonstringyes
outcome.instantRugbooleanyes
outcome.endPricenumberyesa JSON number (a decimal string on the public /fair)
outcome.maxMultipliernumberyesa JSON number (a decimal string on the public /fair)
fairnessobjectyesthe generator inputs, so the round can be re-derived without trusting us; the nullable ones are null on rounds written before the verifiable-round migration
fairness.seedstringyes
fairness.seedHashstringyes
fairness.clientSeedstring · nullableyes
fairness.generatorVersionstring · nullableyes
fairness.configHashstring · nullableyes
fairness.publicPathstringyesthe public /fair/<roomId>/<nonce> path — the same inputs, unauthenticated; add /verifica for the HTML page to send a player to
totalsobjectyestwo sums, never a ratio; null (not zero) on rounds written before the round-totals migration
totals.wagerednumber · nullableyes
totals.paidOutnumber · nullableyes
betsarray of objectyesthe caller's own positions that closed in this round, won or lost; a stake refunded with /rollback is not listed (see /b2b/bet/{id})
bets[].betIdBetIdyes
bets[].amountMoneyyes
bets[].payoutMoneyyes
bets[].netnumberyespayout − amount
bets[].statusstring · one of CASHED_OUT, RUGGEDyes
bets[].cashoutMultipliernumber · nullableyes
bets[].currencyCurrencyyes
bets[].settledAtstring · date-timeyes

OperatorBet

FieldTypeRequiredDescription
betIdBetIdyes
playerIdPlayerIdyes
roundIdRoundIdyes
roomIdstringyesthe table the stake was played at: the room id, or <room>--t<n> in a room with tables
amountMoneyyes
currencyCurrencyyes
statusstring · one of OPEN, REJECTED, SETTLED, ROLLBACK_PENDING, ROLLED_BACKyesthe stake's state towards your wallet — see the endpoint description
createdAtstring · date-timeyes
updatedAtstring · date-timeyes
winobject · nullableyesthe win obligation towards your wallet; null when none was ever queued
win.statusstring · one of PENDING, SETTLED, FAILEDyesPENDING = still retrying with backoff; SETTLED = your /win answered 200; FAILED = no more automatic retries (attempts exhausted, or a registry problem such as a currency change), handled by hand
win.amountMoneyyes
win.multipliernumber · nullableyesclosing multiplier (0 = lost)
win.attemptsintegeryesretries from the outbox: the first call is not one of them, the retry that succeeded is
win.lastErrorstring · nullableyeswhat the last failed attempt got: ERROR for any answer other than a signed 200 (the reason in your body is not kept), CURRENCY_MISMATCH or OPERATOR_NOT_REGISTERED; kept after SETTLED
timelinearray of objectyeswhat the server received for this bet, and the exits it executed itself, in arrival order
timeline[].kindstring · one of PLACE_BET, CASHOUT, CANCEL_BET, SET_ORDERS, SWITCH_BET, AUTO_CASHOUT, RUG_SETTLEyesSET_ORDERS = take profit or stop loss set; SWITCH_BET = Hypnofrog fly switch; AUTO_CASHOUT = an exit the server executed (take profit, stop loss, forced exit at the cap or the time limit); RUG_SETTLE = the position was still open at the loss
timeline[].receivedAtstring · date-timeyes
timeline[].epochinteger · nullableyes
timeline[].pricenumber · nullableyes
timeline[].outcomestring · one of ACCEPTED, REJECTEDyes
timeline[].reasonstring · nullableyeswhy a command was refused, or why the server closed the position (TAKE_PROFIT, STOP_LOSS, RUG…)
timeline[].multipliernumber · nullableyes
timeline[].payoutnumber · nullableyes

ReportResponse

FieldTypeRequiredDescription
operatorIdstringyes
windowobjectyesthe window actually used (from inclusive, to exclusive) and its label
window.fromstring · date-timeyes
window.tostring · date-timeyes
window.labelstringyes
rowsarray of <a href="#schema-reportrow">ReportRow</a>yesone row per (currency, day of settlement); newest day first, then by currency
subtotalsarray of objectyesone per currency over the rows above — never a grand total, and no player count
subtotals[].currencystringyes
subtotals[].betintegeryes
subtotals[].wagerednumberyes
subtotals[].payoutnumberyes
subtotals[].ggrnumberyes
truncatedbooleanyestrue when the row cap (20000 by default) was reached: narrow the window

ReportRow

FieldTypeRequiredDescription
currencystringyescurrency code (? for a bet without one)
daystring · dateyesday of settlement (UTC)
betintegeryesnumber of bets
playerintegeryesdistinct players
wagerednumberyessum of stakes
payoutnumberyessum of payouts
ggrnumberyeswagered − payout

OperatorEvent

FieldTypeRequiredDescription
eventstring · one of onboarding_at_riskyes
roomIdstringyesthe room you launched, never a table id
playerstring · max 128 charsyesthe player id exactly as you sent it in the launch — never our internal id
betsintegeryesclosed bets without a cashout in this session
atstring · date-timeyesISO 8601

OperatorRegistryEntry

One entry of the RGS operator registry — what we configure for you at onboarding, and what several endpoints above depend on. Not an API payload. A walletBaseUrl that is not https (http is accepted on loopback only) drops the whole entry at boot; every other invalid field is dropped on its own, with a log line, and the entry stays. A currency we cannot pay refuses the boot.

FieldTypeRequiredDescription
idstring · pattern ^[A-Za-z0-9_-]{1,64}$yesthe operator id: operator on the launch and the reads, x-operator on the callbacks. An entry with any other id is dropped at boot
secretstringyesthe shared secret: signs our callbacks and the events webhook, verifies your launches and reads
walletBaseUrlstring · uriyeshttps base of the four callbacks (/bet, /win, /rollback, /balance are appended); http only on loopback
currencystring · default EURone per operator, on every callback. Must be a code we know with at most two decimals: more than two (BTC, KWD) or an unknown code is refused at boot; zero decimals (JPY) is accepted, amounts then travel as integers and the room limits (minBet, maxBet, maxWin) are set in that currency
allowedOriginsarray of stringthe origins allowed to iframe your REAL_MONEY rooms (Content-Security-Policy: frame-ancestors): https://host[:port], origin only — an entry with a path or without https is dropped. The browser checks every ancestor frame, so a page wrapping your lobby needs its origin here too. Absent = only the RGS itself, fail-closed: a launch opened inside your lobby stays blank until the origin is declared. Demo rooms can be framed by anyone
enabledboolean · default truefalse = suspended: launches and reads answer 403 OPERATOR_SUSPENDED, no new session opens; positions already open stay playable and cashable. A value that is not a boolean ("false" in quotes) also suspends
ipAllowlistarray of stringIPs allowed to call /b2b/*: exact IPv4 or IPv6 addresses, or IPv4 CIDR blocks (no IPv6 prefixes). Absent or empty = no filter. A defence in addition to the signature, never instead of it
walletTimeoutMsnumber · default 4000per-attempt timeout of every callback (and of the events webhook)
walletRetriesinteger · min 0 · max 5 · default 1retries on network error or timeout only
limitsobjectper-operator caps, positive numbers: they only tighten a room's own limits — the lower of the two applies (the room's default when it sets none), a registry value can never raise a room
limits.maxBetnumber
limits.maxWinnumber
limits.maxExposurenumber
eventsWebhookUrlstring · urihttps (http on loopback only) URL that receives the events webhook; absent = no events are sent