LivePlay Bingo Integration Docs

Direct integration

Wallet contract for casinos integrating with us directly

This page describes the current direct-integration contract. We'll confirm your endpoint URLs and operator settings during onboarding. The shape follows the standard seamless wallet API pattern: 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. We generate sessionid, roundid, and transid; you provide playerid and externalsessionid. Wallet amounts use the default integer-cents convention unless we agree on a custom convention during onboarding. See Money.

Authentication is HTTP Basic Auth by default. On request, we can sign wallet calls instead: we send an x-operator-signature header holding the HMAC-SHA256 of the raw request body (hex encoded), using a secret we share with you. Check the signature against the exact bytes you received.

Endpoints you must implement

EndpointWhen we call it
POST /getbalanceBefore a card purchase, when the player opens the slot panel, and about every 30 seconds while the player is connected
POST /debitWhen the player buys cards, places a side bet, or spins a slot
POST /creditWhen a round or spin settles, including with creditamount: 0 for no win (terminal call)
POST /reverseWhen a debit must be rolled back (the round failed, or the debit itself failed). Can also be the terminal call that closes a failed round

We do not call /endsession. Sessions on our side expire passively after their TTL (24 hours). If you need a positive session-end signal, discuss that requirement during onboarding.

POST /getbalance

Request (from us)

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

By default, getbalance does not carry a currency field. Custom integrations may receive it when their wallet requires a per-currency balance lookup.

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 placeholder or arbitrary non-zero value when you do not track bonus funds, because the value is shown directly to the player.

We read the same cashbalance / bonusbalance / currency trio from the /debit, /credit, and /reverse responses below and refresh the player's UI from them the same way. So return bonusbalance consistently on those responses too (or 0) — if you omit it there while tracking bonus funds, the player's bonus display drops to 0 after every bet and win until the next /getbalance.

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": "live-bingo",
  "currency": "EUR",
  "roundid": "<uuid>",
  "transid": "<uuid>",
  "debitamount": 100,
  "reason": "REGULAR",
  "roundstarted": true,
  "roundended": false
}

reason is REGULAR for paid rounds and FREEBET for free-card rounds. See Free cards for the additional fields on a free-card debit.

A slot spin's debit may also carry "autoplay": true when the player's autospin placed the bet. See Slots.

debitamount: 100 means 1.00 in the player's currency. Wallet amount conversion happens in our integration adapter; current direct integrations use this integer-cents convention.

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. A slot spin is its own round, so its single debit carries roundstarted: true.

Success response

{
  "cashbalance": 12400,
  "bonusbalance": 0,
  "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": "live-bingo",
  "currency": "EUR",
  "roundid": "<uuid>",
  "transid": "<uuid>",
  "creditamount": 250,
  "reason": "REGULAR",
  "roundstarted": false,
  "roundended": true
}

Response

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

The balance fields use the same integer-cents convention: cashbalance: 12650 means 126.50.

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);
  • a round is cancelled before it starts, because some games skip a round when too few players joined (every debit in that round is reversed);
  • a debit got a server error or timed out, so we can't tell whether you took the money;
  • a debit was accepted but the follow-up operation could not complete.

Request (from us)

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

By default, reverse omits currency, reason, and roundstarted — the original debit already established those, and the reverse is identified by originaltransid. Custom integrations may receive currency when their wallet requires a per-currency reverse lookup.

Response

{
  "cashbalance": 12500,
  "bonusbalance": 0,
  "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.
  • Note that we may send a reverse for a debit you never received: when a debit times out we reverse it without knowing whether it arrived, and the reverse can reach you before a late debit does. If you reject a reverse (or it fails), we keep sending the same reverse, same transid, about every 5 minutes for about 3 hours. So if the late debit lands while the round is still open, the next retry undoes it. If the debit never arrives, or the round is already closed, you will see these retries until they stop and we check the round with you.
  • 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 — generated by us, unique per player per round (never shared between players, even in the same room). Store it as an opaque string, not a UUID. Most round IDs are UUIDs, but some games add a prefix: a slot spin is slots:<uuid>. Today no round ID is longer than 44 characters.
  • transid — UUID, generated by us, unique per wallet call. Idempotency key. When we retry a call, we reuse its transid.
  • A bingo 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.
  • A slot spin receives exactly one debit. See Slots.

Free cards

Free card grants and the FREEBET wallet calls they cause are described on their own page: Free cards.

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 when you refuse a call:

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

We read the errorcode field exactly, and we classify your response like this:

Your responseWhat we do
2xx without errorcodeSuccess
2xx with an errorcodeRefusal (the call failed, no retry, except a reverse, which we keep resending in the background)
4xxRefusal, no retry, except a reverse (as above). We use your errorcode if present
5xx, or no response within the timeoutServer failure. credit and reverse are retried (see below)

Never return a 2xx with { code, message } for a refusal. We don't read code, so we would treat the call as a success.

Codes we currently treat specially:

CodeWhat we do
NOT_SUFFICIENT_FUNDSReject the bet, show the player "Insufficient balance."
BET_LIMIT_REACHEDReject the bet, show the player "You have reached your betting limit."
GAMING_LIMIT_REACHEDSame as BET_LIMIT_REACHED
SESSION_NOT_FOUNDShow the player "Your session has expired. Please reconnect."

Any other errorcode you return is logged but treated as a generic refusal from your side: the bet fails and the player sees a generic error. You may return codes like PLAYER_BLOCKED (self-excluded) or CURRENCY_MISMATCH for your own records; today they are not specially handled.

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
/credit for a bingo round3 attempts, with 1s and 2s waits between (~3s span), then a background job resends the same call, same transid, about every 5 minutes for about 3 hours (a refusal is retried too). If a round has two credits and the first fails, the second (the one with roundended: true) is held back and resent after it, in order
/credit for a slot spin3 attempts, then a background job retries the same call about every 2 minutes until your wallet accepts it (a refusal is retried too). See Slots
/reverse (after a failed /debit)Same as credit (3 attempts), then a background job resends the same call about every 5 minutes for about 3 hours (a refusal is retried too)
/reverse during round-fail cleanup (game crash, round cancelled before it starts)Single attempt with 2s timeout, then the same background job as above
/debitNo retry — single attempt. A failed debit triggers a retried /reverse 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 for bingo. We send one before each card purchase, AND one about every 30 seconds for every connected player while their iframe is open (the connection 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–2 calls per player per bingo round (card purchase + optional side bet)
  • credit: 1 call per player per bingo round (settlement), 2 for a free-card round with a side bet
  • reverse: only on failure paths; rare in steady state
  • Slots: 1 debit and 1 credit per spin. With autospin, that's a pair every few seconds per spinning player. See Slots.

A 100-player bingo room with rounds back-to-back generates roughly 250–400 wallet calls per minute at peak, dominated by getbalance, before any slot spins. 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