Skip to content

Data model & enums

The field-level rules behind the wire types used across the Operator API and the Wallet callback API. This page is the semantics; the two API references have the exact request/response shapes for each endpoint.

Amounts & currency

  • Amounts are always integers in the currency's minor unit (e.g. cents for USD) — never floats. 500 means $5.00. This applies everywhere an amount appears: TransactionRequest.amount, TransactionResponse.balance, the balance callback's balance, and every betAmountMinor/amountMinor/ balanceMinor field across both APIs and the shell bridge.
  • currency must be an uppercase, 3-letter ISO-4217 code (e.g. "USD", not "usd") — the platform's validator is case-sensitive. A malformed or lowercase code is rejected with 400.

TransactionType

ts
type TransactionType = 'BET' | 'WIN' | 'ROLLBACK' | 'ADJUSTMENT'

BET, WIN, and ROLLBACK are what you'll see in practice — submitted by a game studio's server and forwarded to your wallet callback API. ADJUSTMENT exists in the platform's canonical model for a rare, platform-internal admin action (a manual balance correction); handle it the same way as any other type (apply amount in the direction given by direction).

TransactionRequest

The body your POST /v1/wallet/transaction implementation receives:

FieldTypeNotes
transactionIdstringGenerated by the provider. Reused verbatim on retry — this is the idempotency anchor your implementation must key off.
sessionTokenstringOpaque to you — identifies the session your own POST /v1/operator/games/launch call created. Pass it through unchanged; there's no separate operatorId/playerRef field you need to cross-check it against.
typeTransactionType'BET' | 'WIN' | 'ROLLBACK' | 'ADJUSTMENT'
roundIdstringGroups the transactions belonging to one round.
roundCompletebooleantrue on whichever of BET/WIN ends the round. Meaningful only for BET/WIN; ignored for ROLLBACK/ADJUSTMENT.
originalTransactionIdstring?Present when type === 'ROLLBACK' — the BET's transactionId being reversed. Absent for every other type.
playerRefstringYour internal player identifier, the same one you passed to launchGame.
amountnumber (int64)Minor units, >= 0. A zero-amount WIN (no payout) is valid.
currencystringUppercase ISO-4217, matches the currency the session was launched with.
gameIdstringMatches the session's game.
direction'DEBIT' | 'CREDIT'?Present only for ADJUSTMENT — which way to move the balance. Absent for BET/WIN/ROLLBACK (their direction is implied by type).
metadataobject?Opaque, capped at 8 KiB — see below.

Metadata & jackpot payouts

metadata is a provider-supplied JSON object the platform stores and forwards to you verbatim, without interpreting it. It's optional, and the platform accepts it on any transaction type — the typical use is attaching jackpot detail to a WIN.

  • Must be a JSON object — an array, string, number, or null is rejected by the platform before it ever reaches you.
  • Capped at 8 KiB.
  • The platform never reads any key inside it — display or store it as you see fit; there's nothing you're required to act on.
ts
// A documentation-only convention for a jackpot WIN's metadata — the
// platform does not enforce this shape, and neither should you. Any JSON
// object is accepted; treat unrecognized shapes as "nothing to show."
type JackpotMetadata = {
  jackpot?: {
    won: boolean
    tier?: string
    amountMinor?: number
    poolId?: string
  }
}

TransactionResponse

What your implementation returns:

ts
type ResponseStatus = 'OK' | 'DECLINED'
type TransactionResponse = { status: ResponseStatus; balance: number }

DECLINED is only ever a valid outcome for a BET (insufficient funds, or outside your own configured bet limits) — it's a normal business result, not an error. WIN, ROLLBACK, and ADJUSTMENT must never be declined: if you can't apply one of those, respond with a non-200 status instead and let the platform's retry/reconciliation machinery handle it — see Errors & retry.

Session

The mapping POST /v1/operator/games/launch creates, and which sessionToken on every subsequent wallet call resolves back to:

ts
type Session = {
  token: string
  playerRef: string
  operatorId: string
  providerId: string
  gameId: string
  currency: string
  language: string
  createdAt: string
  expiresAt: string
  demo: boolean
}

You never see this shape directly — it's server-side platform state. It's documented here because every field on TransactionRequest that looks like it should be cross-checked (playerRef, gameId, currency) is already validated against it before the request reaches you.

Round ownership

A roundId is only unique within a (providerId, operatorId) pair — the same roundId string can exist under a different operator, or under a different game from a different provider, without colliding. This is why POST /v1/operator/rounds/replay scopes its lookup to your own operator tenant, and why a roundId you receive on a TransactionRequest is only ever meaningful paired with the gameId on the same request.

Ledger direction

Player balance delta = Σ(CREDIT) − Σ(DEBIT). BET debits, WIN credits, ROLLBACK reverses a BET (credits back), and ADJUSTMENT carries its direction explicitly. You don't need to derive this yourself — it's context for reading your own ledger against what the platform expects to see.