LivePlay Bingo Integration Docs

Direct integration

Wallet contract for casinos integrating with us directly

Direct integration is forward-looking. The schemas here are concrete enough to scope work, but talk to us before starting — we'll confirm the final paths. The shape mirrors Gametech's (VisionLINK's) seamless wallet API: we call your wallet in real time per transaction, rather than transferring a balance to us up-front.

What direct means

Instead of an aggregator handling the wallet, you implement wallet endpoints on your side and we call them. You retain full ownership of the player wallet, identity verification (Know Your Customer, KYC), and responsible gambling limits. You skip the aggregator fee.

You implement four endpoints. We call them when game events happen. All authentication is HTTP Basic Auth, all amounts are integers in the currency's smallest unit (see Money), all identifiers are UUIDs we generate.

Endpoints you must implement

EndpointWhen we call it
POST /getbalanceBefore showing the card-purchase UI
POST /debitWhen the player buys cards or places a side bet
POST /creditWhen the player wins, or to close a zero-win round (terminal call)
POST /reverseWhen a debit must be rolled back (game crash, validation failure). Can also be the terminal call that closes a failed round

We do not currently call /endsession. Sessions on our side expire passively after their TTL (24 hours by default). If you need a positive session-end signal, talk to us — it's on the roadmap.

POST /getbalance

Request (from us)

{
  "playerid": "player_42",
  "sessionid": "<our-session-id>",
  "externalsessionid": "<your-session-id>",
  "gamecode": "BINGO25"
}

getbalance does not carry a currency field — it returns the balance for whichever currency the player's wallet holds for this session.

Response

{
  "cashbalance": 12500,
  "bonusbalance": 0,
  "currency": "EUR"
}

We read cashbalance and currency for our own checks. bonusbalance is forwarded to the player's UI as a separate displayed value — return your real bonus-funds figure if you track it, otherwise return 0. Don't return a fake non-zero value: it will show up incorrectly on the player's screen.

POST /debit

Place a bet. Deduct from the player's wallet.

Request (from us)

{
  "playerid": "player_42",
  "sessionid": "<our-session-id>",
  "externalsessionid": "<your-session-id>",
  "gamecode": "BINGO25",
  "currency": "EUR",
  "roundid": "<uuid>",
  "transid": "<uuid>",
  "debitamount": 100,
  "reason": "REGULAR",
  "roundstarted": true,
  "roundended": false
}

reason is always REGULAR today. The field is reserved for marking promotional rounds (e.g., freebets) when those land — for now you can ignore it; we will never send any other value.

Side-bet debits arrive with roundstarted: false. A round starts on the first card-buy debit (roundstarted: true); subsequent debits within the same round (e.g. lucky line side bets) carry roundstarted: false.

Success response

{
  "cashbalance": 12400,
  "currency": "EUR"
}

Failure response

{ "errorcode": "NOT_SUFFICIENT_FUNDS", "errormessage": "balance below stake" }

Idempotency

A repeated call with the same transid MUST return the same result — same balance, same status. Do not deduct twice. Treat transid as the deduplication key.

POST /credit

Award a win. Add to the player's wallet.

Request (from us)

{
  "playerid": "player_42",
  "sessionid": "<our-session-id>",
  "externalsessionid": "<your-session-id>",
  "gamecode": "BINGO25",
  "currency": "EUR",
  "roundid": "<uuid>",
  "transid": "<uuid>",
  "creditamount": 250,
  "reason": "REGULAR",
  "roundstarted": false,
  "roundended": true
}

Response

{
  "cashbalance": 12650,
  "currency": "EUR"
}

Important rules

  • Every round MUST be closed exactly once, by a single terminal call with roundended: true. The terminal call is either a credit (with the win amount, or creditamount: 0 for a no-win round) or a reverse (when the round failed mid-play). Never both.
  • Reject any non-idempotent call against a round you've already closed.
  • Idempotent on transid.

POST /reverse

Roll back a previously-issued debit. We call this when a game round fails mid-play (state machine error, crash) or a debit was accepted but the followup operation could not complete.

Request (from us)

{
  "playerid": "player_42",
  "sessionid": "<our-session-id>",
  "externalsessionid": "<your-session-id>",
  "gamecode": "BINGO25",
  "roundid": "<uuid>",
  "transid": "<uuid>",
  "originaltransid": "<the-debit-transid-being-reversed>",
  "roundended": true
}

Reverse intentionally omits currency, reason, and roundstarted — the original debit already established those, and the reverse is identified by originaltransid.

Response

{
  "cashbalance": 12500,
  "currency": "EUR"
}

Rules

  • originaltransid MUST refer to a previously-accepted debit for the same player and round. If you don't recognise it, return an error.
  • Reverses are idempotent on transid — a duplicate retry returns the same result.
  • A reverse against an open round (one we have not yet closed with roundended: true): apply the reverse. If we set roundended: true on this reverse call, also close the round.
  • A reverse against an already-closed round: reject with ROUND_ALREADY_CLOSED. Once closed, no further wallet activity should affect the round. (The exception is a duplicate retry of the same transid — that's idempotent and you should return the original result.)

Round and transaction ID contract

  • roundid — UUID, generated by us, unique per player per round (never shared between players, even in the same room).
  • transid — UUID, generated by us, unique per wallet call. Idempotency key.
  • A round may receive up to two debit calls before it's closed: one for the card purchase and (optionally) one for a side bet (e.g. lucky line). Each is a fresh transid; roundid stays constant.

Worked timing examples

Happy path — player wins:

POST /getbalance  { player: P1 }                                          → balance 5000
POST /debit       { round: R1, trans: T1, debit: 100, ended: false }      → balance 4900
POST /credit      { round: R1, trans: T2, credit: 250, ended: true  }     → balance 5150
                                                                             round R1 closed

Idempotent retry — we didn't get your credit response, we retry:

POST /credit  { round: R1, trans: T2, credit: 250, ended: true } → balance +250
POST /credit  { round: R1, trans: T2, credit: 250, ended: true } → SAME response,
                                                                    no second add

Zero-win round — required to close it:

POST /debit   { round: R1, trans: T1, debit: 100, ended: false }   → balance -100
POST /credit  { round: R1, trans: T2, credit: 0, ended: true   }   → balance unchanged
                                                                      round R1 closed

Game crashed mid-round — we reverse and close:

POST /debit   { round: R1, trans: T1, debit: 100, ended: false }       → balance -100
POST /reverse { round: R1, trans: T2, originaltransid: T1, ended: true } → balance +100
                                                                            round R1 closed

Late reverse against a closed round — reject:

POST /credit  { round: R1, trans: T2, credit: 250, ended: true } → round R1 closed
POST /reverse { round: R1, trans: T3, originaltransid: T1 }      → ROUND_ALREADY_CLOSED

Side-bet reverse keeps the round open:

POST /debit   { round: R1, trans: T1, debit: 100, ended: false }  → cards bought
POST /debit   { round: R1, trans: T2, debit: 25,  ended: false }  → lucky line bet
POST /reverse { round: R1, trans: T3, originaltransid: T2,
                ended: false }                                     → lucky line refunded,
                                                                     round R1 still open
POST /credit  { round: R1, trans: T4, credit: 0, ended: true }    → round R1 closed

Error model we expect from you

Return a JSON body with errorcode and errormessage fields on any non-2xx response:

{ "errorcode": "NOT_SUFFICIENT_FUNDS", "errormessage": "balance below stake" }

We read the errorcode field exactly. Do not return { code, message } — our wallet client will not parse it and will treat the call as a transport error, triggering retries you don't want.

Codes we currently treat specially:

CodeWhat we do
NOT_SUFFICIENT_FUNDSReject card purchase, surface a clear message to the player
BET_NOT_ALLOWEDReject card purchase, surface "this stake is not allowed for you right now" to the player
SESSION_NOT_FOUNDTreat as session-expired, surface to the player

Any other errorcode you return is logged but treated as a generic business error from your side — the round fails and the player sees a generic error. You may return codes like PLAYER_BLOCKED (self-excluded, hit a limit) or CURRENCY_MISMATCH for completeness; today they are not specially handled, but we expect to layer behaviour onto them as the integration matures.

Network timeouts and 5xx responses are retried per the table above (only for credit and reverse).

Only credit and reverse retry. debit and getbalance are single-shot. 5xx responses (server-side HTTP errors, status 500–599) and network timeouts trigger:

EndpointRetries on 5xx / timeout
/creditYes — 3 attempts total, with 1s and 2s waits between (~3s span)
/reverse (after a failed /debit)Same as credit (3 attempts)
/reverse during round-fail cleanup (game crash)No retry, single attempt with 2s timeout
/debitNo retry — single attempt. A failed debit triggers a retried /reverse (3 attempts) to return funds
/getbalanceNo retry — single attempt

The default per-call HTTP timeout is 10 seconds. Be idempotent on transid — for the calls that do retry, you will receive duplicates.

Wallet call rate

Direct integrators should size for the following peak rates per operator. These are guidance, not enforced limits — talk to us if you anticipate sustained traffic well above this:

  • getbalance: the dominant call. We send one before each card purchase, AND one every 30 seconds for every connected player while their iframe is open (the WebSocket heartbeat). A 100-player room generates roughly 200 getbalance calls per minute from heartbeats alone, on top of the per-round pre-buy calls.
  • debit: 1–3 calls per player per round (card purchase + optional side bet)
  • credit: 1 call per player per round (settlement)
  • reverse: only on failure paths; rare in steady state

A 100-player room with rounds back-to-back generates roughly 250–400 wallet calls per minute at peak, dominated by getbalance. Confirm your specific volume expectations during onboarding.

Voids and disputes after a round closes

We have no API to undo a closed round. reverse only works while the round is still open. Once a round is closed by a terminal credit or reverse, it is final from our perspective.

If a regulator orders a void after the fact, or a player wins a chargeback against a settled round, the casino is responsible for making the adjustment manually in its own books. We can supply the round record (from /gamelauncher/replay) for evidence; we cannot reverse the wallet entries.

Sandbox

When you're ready to start, we'll provision a sandbox callback URL pair (yours → ours, ours → yours), exchange Basic Auth credentials, and run a fixed test suite that exercises every endpoint above including failure paths. Talk to us when you're ready.

On this page