Skip to content

Config + Ledger

Config + Ledger is Surface 2: Service → Platform over HTTP. These are platform endpoints that the game service calls as a client. Auth depends on the endpoint:

EndpointHeader
GET /internal/runtime-profiles/:idx-internal-config-secret
GET /internal/bet-templatesx-internal-config-secret
POST /internal/consolidated/txx-internal-wallet-secret
POST /internal/consolidated/tx/settlex-internal-wallet-secret
POST /internal/engines/round-start-checkx-internal-control-secret

The full contents of a runtime profile. The service calls it when bringing an engine up or each round, and caches the result (so a temporarily-unreachable platform does not stall rounds).

This is distinct from the desired-state heartbeat: desired-state is pushed to the service every 60s (“which engines should run”); runtime-profiles/:id is pulled by the service at the frequency of its own round logic.

Headers: x-internal-config-secret: <secret>

Response (200):

{
"ok": true,
"protocolVersion": "1.1",
"profile": {
"id": 4,
"name": "Default",
"service_id": 3,
"bot_min_count": 3,
"bot_max_count": 7,
"bot_bet_currency": "FUN",
"demo_currency": "FUN",
"demo_start_balance": 10000,
"save_bot_bets": 1,
"save_demo_bets": 0,
"config_json": {
"rtpTarget": 0.96,
"maxCards": 5,
"bettingMs": 6000
}
},
"botCurrencies": {
"source": "default_mix",
"codes": ["AZN", "BRL", "EUR", "GBP", "USD"],
"fallback": "FUN",
"reportingCurrency": "EUR",
"ratesToReporting": { "USD": 0.92, "BRL": 0.18, "GBP": 1.17 },
"asOf": 1716800000000
}
}

Top level:

FieldDescription
oktrue on success. On error: { ok: false, message: "..." }.
protocolVersionCurrently "1.1". v1.0 services ignore it.
profileThe runtime-profile row, as raw snake_case DB columns.
botCurrenciesPlatform-managed currency mix for bot bets.

profile fields (DB column → JSON snake_case):

FieldDescription
id, name, service_idRecord metadata.
bot_min_count, bot_max_countRange of bots to seat per round (fake players that make a round feel live).
bot_bet_currencyThe currency bots bet in (usually FUN; with a mix, chosen from botCurrencies).
demo_currency, demo_start_balance, save_demo_betsDemo-mode behavior. save_* are SMALLINT (0/1) in the DB, integers in JSON.
save_bot_betsWhether bot bets are written to the DB.
config_jsonEngine-opaque config. Undefined to the platform; the keys are defined and validated by the service (Cloud1x2’s convention is camelCase). The platform only stores and returns it.

botCurrencies fields:

FieldDescription
sourceHow the list was computed (default_mix = currencies flagged in_default_mix).
codesCurrency codes a bot may pick (uppercase, 3-letter).
fallbackUsed when codes is empty / selection fails (FUN).
reportingCurrencyThe currency the platform aggregates into.
ratesToReportingEach code → reporting-currency rate (FX).
asOfEpoch-ms timestamp of the latest rate snapshot.

Naming note: profile carries snake_case (a raw DB row, pre-CGSP schema); config_json keys are camelCase (engine-owned); botCurrencies is camelCase (the CGSP wire convention).

Errors: 401 (wrong config secret), 404 (no profile with this id).

Opens a ledger record when a bet is placed. The record is written to the platform’s transactions table and is the single source of truth. Idempotent on (engineType, serviceBetUid) — re-posting the same pair returns the existing record.

Headers: x-internal-wallet-secret: <secret>

Minimum body:

{
"engineType": "wp9dvdgp4wfqmxq4",
"serviceBetUid": "wp9dvdgp4wfqmxq4-01krrh7xn3qq5e69gn58wkjt38",
"gameExternalId": 9000,
"status": "open"
}

Full body (production):

{
"engineType": "wp9dvdgp4wfqmxq4",
"serviceBetUid": "wp9dvdgp4wfqmxq4-01krrh7xn3qq5e69gn58wkjt38",
"gameExternalId": 9000,
"partnerId": 1,
"operatorId": "cx-12",
"playerToken": "d315fa12770e65eb4af38c589aed0d",
"customer": "3773",
"currency": "EUR",
"betAmount": 10.00,
"winAmount": 0,
"reportingCurrency": "EUR",
"rateToReporting": 1.0,
"debitTrxId": "debit-wp9dvdgp4wfqmxq4-01krrh7xn3qq5e69gn58wkjt38",
"creditTrxId": null,
"rollbackTrxId": null,
"status": "open",
"isDemo": false,
"walletB2bLog": {
"kind": "debit",
"entry": { "request": {}, "response": {}, "requestTimestamp": "...", "responseTimestamp": "..." }
}
}
FieldDescription
engineTypeThe service’s service_uid (the wire identity).
engineType + serviceBetUidThe idempotency key. Re-posting returns the existing record.
gameExternalIdThe bet’s game (canonicalGameExternalId, or the real game in the bucket).
partnerId, operatorIdThe partner/operator side (from verify-launch).
playerToken, customerPlayer identity (from the operator).
currency, betAmountThe bet’s currency and stake.
winAmount0 at open; updated on settle.
reportingCurrency, rateToReportingReporting normalization; usually equal to currency.
debitTrxIdThe trxId from the wallet debit.
creditTrxId, rollbackTrxIdnull at open.
statusA CHECK-constraint enum: 'open' | 'won' | 'lost' | 'cancelled' | 'rolled_back'. 'open' at open. Any other string → 500 (DB constraint).
isDemoWhether this is a demo bet.
walletB2bLogOptional wallet-call audit log (request + response); appended to transactions.wallet_api_log.

Response (200):

{ "ok": true, "id": 12345 }

id is the transactions bigint primary key.

Errors: 400 (missing engineType/serviceBetUid), 401 (wrong secret), 500 (DB error — retry after a few seconds).

Demo mode: with isDemo: true the record is still written and flagged is_demo; if save_demo_bets is false on the profile, the platform skips it (still returning 200).

Updates the ledger record when a bet settles.

Headers: x-internal-wallet-secret: <secret>

Request body:

{
"engineType": "wp9dvdgp4wfqmxq4",
"serviceBetUid": "wp9dvdgp4wfqmxq4-01krrh7xn3qq5e69gn58wkjt38",
"status": "won",
"winAmount": 19.50,
"creditTrxId": "credit-wp9dvdgp4wfqmxq4-01krrh7xn3qq5e69gn58wkjt38",
"rollbackTrxId": null,
"walletB2bLog": {
"kind": "credit",
"entry": { "request": {}, "response": {}, "requestTimestamp": "...", "responseTimestamp": "..." }
}
}
FieldDescription
engineType + serviceBetUidThe same pair as open; locates the record.
statusA terminal value: 'won' | 'lost' | 'cancelled' | 'rolled_back' (note the underscore — 'rolled_back', not 'rollback').
winAmountThe win (0 on a loss).
creditTrxIdThe credit trxId. A loss also calls credit (amount=0) to keep the debit/credit pair symmetric for operator reconciliation — so creditTrxId is set even on a loss, with winAmount: 0.
rollbackTrxIdSet if rolled back.
walletB2bLogThe credit/rollback audit log.

Response (200): { "ok": true }.

Idempotent: settling the same bet twice makes the second call a no-op — safe when a round crashes and retries.

(v1.1) A per-round pull of the platform’s suspend lists. The service calls it at the start of each round, before broadcasting round-start, and applies the returned lists at round end (so an operator-suspend decision lands on a round boundary). A paused engine does not call it (it has no round to gate).

Headers: x-internal-control-secret: <secret>

Request body:

{
"serviceUid": "wp9dvdgp4wfqmxq4",
"engineId": "wp9dvdgp4wfqmxq4:p:42"
}
  • serviceUid must equal the caller’s UID (as received in desired-state.serviceUid); a mismatch returns 403 so a misconfigured service cannot query another service’s engines.
  • engineId must be one previously delivered via desired-state; an unknown engine returns 404 (likely a race — the engine was just removed), which the service treats as “do not start a new round” and trusts the next heartbeat.

Response (200):

{
"suspendedPartners": [1001, 1002],
"suspendedOperators": [
{ "partnerId": 1, "operatorId": "op1" }
]
}
  • suspendedPartners — organizationId values; active players whose partner is in this list are disconnected at the current round’s end. New sessions are already rejected by verify-launch.
  • suspendedOperators — same semantics at (partnerId, operatorId) granularity.

The two lists are additive: a player is disconnected if their partner is in suspendedPartners or their operator is in suspendedOperators. The response covers only platform-owned dynamic suspends — game-level suspend (memberGames[].suspended) comes via desired-state, and engine-level pause is service-owned (see Control Plane). Player identity never leaves the service; the service categorizes its own sockets.

Failure modes:

  • 403 — missing/wrong secret, or serviceUid does not own the engine.
  • 400 — malformed body (missing serviceUid or engineId).
  • 404 — engineId not registered; treat as “do not start a round,” trust the next heartbeat.
  • 5xx / network — keep using the last successful response and continue the round. Cold-start fallback: empty lists (keep serving) — platform access problems must never silently stop round emission.

Optional: skipping this endpoint disables suspend-kick but does not crash the service; v1.0 services do not know about it.

(v1.1) A side-effect-free bet-template fetch. The service calls it at the start of each round (per player) and reflects betMin / betMax / maxWin / quickBets to active sockets for the next round. This is the canonical endpoint for the per-round bet-template refresh.

It is the bet-template sub-clause of verify-launch, but without the side effects: it does not call operator wallet authenticate (no TOKEN_TIMEOUT risk), does not upsert the player, and does not rebuild partner config. It is a pure DB lookup using the same resolution chain (operator-game override → operator-service pin → partner default).

Headers: x-internal-config-secret: <secret> (shared with runtime-profiles — both are read-only config endpoints).

Query parameters:

FieldTypeRequiredDescription
serviceUidstring✓The caller’s 16-char wire identifier (services.service_uid)
partnerIdinteger✓The operator’s partner (partners.id)
operatorIdstring✓The operator’s external id (operators.operator_id)
currencystring✓ISO 4217, uppercase (e.g. "EUR")
gameExternalIdintegeroptionalFor per-game override lookup; omitted = service default

Example:

GET /internal/bet-templates?serviceUid=wp9dvdgp4wfqmxq4&partnerId=1&operatorId=cx-12&gameExternalId=9000&currency=EUR
x-internal-config-secret: <secret>

Response (200):

{
"ok": true,
"betMin": 1,
"betMax": 100,
"maxWin": 10000,
"quickBets": [1, 5, 10, 50]
}
  • betMin, betMax, maxWin — currency-aware bet limits (maxWin may be null = no cap).
  • quickBets — preset bet buttons (usually four).

Call pattern: once per active player at round start (caching is allowed per unique operator+currency); write the result to the socket and reflect it to the client; on 5xx/network, keep the last successful snapshot (the per-session snapshot from verify-launch is also a valid fallback).

Errors: 403 (wrong secret), 400 (bad query), 404 (serviceUid not an active service), 5xx (DB error).

Optional — verify-launch already returns per-session limits; use this to refresh them per round without the wallet side effects.