Skip to content

Wallet callbacks

The HTTP endpoints your wallet exposes to 19flip, with the exact request bodies we send and the responses we expect. 19flip calls them whenever a game reads a balance or moves money, and (Flip Jet) when a shared game round ends. Your wallet is the only place player money lives.

Overview

All callbacks are POST requests with a JSON body to a path under your callback_url. The path is appended with a single /: callback_url https://casino.example.com/api/providers/19flip gives https://casino.example.com/api/providers/19flip/bet/debit.

Endpoint Money effect Idempotency key Response
POST {callback_url}/balance none – Balance response
POST {callback_url}/bet/debit balance − amount yes Transaction response
POST {callback_url}/win/credit balance + amount yes Transaction response
POST {callback_url}/bet/rollback balance + amount (undo a debit) yes Transaction response
POST {callback_url}/win/rollback balance − amount (undo a credit) yes Transaction response
POST {callback_url}/round/finish none (Flip Jet only) yes any 2xx, see /round/finish

Every request carries Content-Type: application/json, Authorization: Bearer <api_key>, X-API-Key: <api_key> and X-Signature. Verify them as described in Authentication.

Requirements for your endpoints:

  • Answer within 10 seconds. See Idempotency & retries for timeouts and retries.
  • Answer with a flat JSON object. Do not wrap it in {"success": ..., "data": ...}: we read the fields from the top level, and a wrapped answer is read as a balance of 0.
  • Answer 2xx only when the operation was applied (or was already applied under the same idempotency_key). Signal a refusal with a 4xx status. See Errors.
  • Do not redirect. Configure the final URL as callback_url, without a query string.

Calls from the integration test

New in 2.7.0

19flip can run an integration test against your wallet and send you a report.

When 19flip runs the integration test on your account, the same five endpoints receive ordinary signed callbacks with the same headers, one attempt each and a 10-second timeout. You can tell them apart by: idempotency_key starting with itest-, round_id between 900000 and 999999, game_id null, reason "integration test" on rollbacks, and a session_id you did not issue unless you gave us one. One request is deliberately signed with a wrong secret and must get 401.

Common fields

Field Type In Description
player_id string all The player_id you sent in launch-game.
currency string all 3-letter currency of the session.
session_id string all The session_id returned by launch-game (a UUID). The connection test sends "healthcheck".
amount number money calls Always > 0. A JSON number such as 1.5 or 10.0, normally with at most 2 decimal places.
round_id integer money calls The game's round number. Unique only within one game. Usually a JSON integer; operations re-sent manually by 19flip staff carry it as a string of digits (e.g. "48213"), so accept both.
idempotency_key string money calls Unique per intended money movement. Deduplicate on it. Treat it as an opaque string (current keys are up to 64 characters; store at least 255).
game_id string or null debit, credit Game id, e.g. "bingo-bongo". May be null (Block Boost and Mister Twister do not send it). Use session_id to know the game.
transaction_id string or null debit, credit The game's internal reference, usually null. Informational only; your response's transaction_id is a different thing.
original_transaction_id string rollbacks Which operation to undo. Your transaction_id from the original response when 19flip received one; otherwise the original operation's idempotency_key.
reason string or null rollbacks Free text, e.g. "round cancelled", "manual rollback".
game_round_id integer debit, credit, bet rollback (Flip Jet only) The shared game round the bet plays in: the same number as game_round_id in /round/finish. In Flip Jet round_id is per bet, so use this field to group a round's bets. Absent for other games.
multiplier string credit (Flip Jet only) The multiplier at which the bet was cashed out, two decimals, e.g. "2.35". amount = bet × multiplier, rounded down. Absent for other games.
round_type string debit, credit, bet rollback (Flip Jet only) "golden_flight" for a Golden Flight purchase. The debit amount is then the price (base bet × 20, 50 or 100), which may exceed your max_bet. Absent for ordinary bets and for other games.

The fields are always present in the order shown in the examples below; fields that have no value are sent as null. Exception: game_round_id, multiplier and round_type are appended at the end only by the games that send them and are absent otherwise.

Added in 2.73.0

round_type (Flip Jet Golden Flight).

Added in 2.34.0

game_round_id and multiplier (Flip Jet). Ignore unknown fields in callback bodies: new optional fields are added at the end.

Transaction response

Returned by all four money endpoints with HTTP 200:

{
  "transaction_id": "5b0e1c52-3f7e-4d7c-9a8e-0f5d2a61c9e4",
  "balance_before": 100.0,
  "balance_after": 98.5,
  "status": "SUCCESS"
}
Field Type Required Description
transaction_id string yes Your id for this operation. 19flip stores it and sends it back as original_transaction_id when this operation is rolled back. Must be unique in your system.
balance_before number yes Player balance before the operation.
balance_after number yes Player balance after the operation. Games show it to the player.
status string yes SUCCESS. See the note below for other values.

How 19flip reads your answer:

Your answer What 19flip records
2xx, all four fields, "status": "SUCCESS" Operation succeeded.
2xx, status missing or not one of SUCCESS / FAILED / PENDING Recorded as PENDING; it goes to manual reconciliation after 5 minutes.
2xx, "status": "PENDING" Same as above. Avoid it: 19flip has no way to ask you later.
2xx, "status": "FAILED" Recorded as failed, and passed to the game as a normal 2xx answer. Some games treat any 2xx as money moved. Do not use this to decline; answer 4xx instead.
2xx, not JSON, or a JSON envelope Balances read as 0, transaction_id empty, status PENDING.
4xx Definitive refusal; not retried. 402 means insufficient funds.
5xx, timeout, connection error Outcome unknown; retried with the same body and key.

Balance response

Returned by /balance with HTTP 200:

{
  "balance": 100.0,
  "currency": "USD"
}
Field Type Required Description
balance number yes Current balance available for play in the requested currency. If missing, 0 is used.
currency string yes Echo of the requested currency. If missing, the requested currency is used.

POST /balance

Returns the player's current balance. Moves no money and has no idempotency key; games call it often (for example before each bet, and when a lobby opens).

Request body:

{"player_id": "player-123", "currency": "USD", "session_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7b9d10"}
Field Type Required
player_id string yes
currency string yes
session_id string yes

Response: Balance response.

Errors: 404 if the player is unknown to you, 401 on a bad key or signature.

The connection test (session_id = "healthcheck")

19flip staff can test your endpoint from the 19flip admin panel ("test connection"). It sends one signed /balance call:

{"player_id": "healthcheck", "currency": "USD", "session_id": "healthcheck"}
  • player_id defaults to "healthcheck" and currency to "USD"; staff may type a real test player and another currency instead. session_id is always "healthcheck".
  • Timeout 5 seconds, one attempt, no retries.
  • The test passes on any 2xx. It shows the HTTP status, latency and the first 2000 characters of your body.

Recommended: after checking the key and signature, answer session_id == "healthcheck" with 200 and {"balance": 0, "currency": "<requested currency>"} even if no such player exists. Otherwise the test reports your 404 for the unknown player healthcheck as a failure.


POST /bet/debit

Takes a bet from the player's balance.

Request body:

{"player_id": "player-123", "amount": 1.5, "currency": "USD", "round_id": 48213, "session_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7b9d10", "idempotency_key": "bb-tkt-7-48213-9921", "game_id": "bingo-bongo", "transaction_id": null}
Field Type Required
player_id string yes
amount number yes
currency string yes
round_id integer yes
session_id string yes
idempotency_key string yes
game_id string or null yes (may be null)
transaction_id string or null yes (may be null)

Success response (200):

{
  "transaction_id": "5b0e1c52-3f7e-4d7c-9a8e-0f5d2a61c9e4",
  "balance_before": 100.0,
  "balance_after": 98.5,
  "status": "SUCCESS"
}

Insufficient funds (402, nothing debited):

{"error": "insufficient balance", "balance": 1.0}

Rules:

  • Debit atomically: check the balance and debit in one database transaction, with a unique constraint on idempotency_key.
  • A repeat of the same idempotency_key returns the stored answer (the same transaction_id and balances, or the same 402), without debiting again.
  • A round can contain several debits (for example Block Boost debits on every paid action; Bingo Bongo debits once per card purchase).
  • 19flip only forwards bets that pass its own checks: your account is active, the player is not blocked, and the amount is within your minimum/maximum bet. Refused bets never reach you.

POST /win/credit

Adds a win to the player's balance.

Request body:

{"player_id": "player-123", "amount": 12.0, "currency": "USD", "round_id": 48213, "session_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7b9d10", "idempotency_key": "bb-bank-7-48213-551", "game_id": "bingo-bongo", "transaction_id": null}

Fields are the same as for /bet/debit.

Success response (200):

{
  "transaction_id": "9d7f3a10-6b2c-4e8a-8f41-2c6b0e9a7d55",
  "balance_before": 98.5,
  "balance_after": 110.5,
  "status": "SUCCESS"
}

Rules:

  • Always accept a valid win. Do not refuse it because the session is old, the player is offline, the round ended long ago, or the player's account is restricted for new bets. The money is already won; refusing only creates a manual case.
  • Wins can arrive minutes or hours after the player left (for example Bingo Bongo pays jackpots and insurance from a background process, and keeps retrying until you confirm).
  • A round can have several wins for the same player, each with its own idempotency_key (for example a bank prize, a jackpot tier and an insurance payout).
  • A win can go to a player who did not place a bet in that round (Bingo Bongo credits tips to the round's host, who is one of your players).
  • Rounds without a win produce no /win/credit call; amount is never 0.

POST /bet/rollback

Undoes a bet: returns the debited amount to the player.

Request body:

{"player_id": "player-123", "amount": 1.5, "currency": "USD", "round_id": 48213, "session_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7b9d10", "original_transaction_id": "5b0e1c52-3f7e-4d7c-9a8e-0f5d2a61c9e4", "idempotency_key": "bb-rfnd-7-48213-9921", "reason": "round cancelled"}
Field Type Required
player_id string yes
amount number yes
currency string yes
round_id integer yes
session_id string yes
original_transaction_id string yes
idempotency_key string yes
reason string or null yes (may be null)

Success response (200):

{
  "transaction_id": "0c4e9b27-1d8a-4f36-b2e5-7a9c3d1f6e80",
  "balance_before": 98.5,
  "balance_after": 100.0,
  "status": "SUCCESS"
}

Rules:

  • Find the original debit by original_transaction_id, matching either your transaction_id or the original idempotency_key. The idempotency key is used when the original debit timed out and 19flip never learned your id.
  • amount equals the original debit amount.
  • A debit is rolled back at most once. If the original is already rolled back (for example by a manual rollback with a different key), return 200 with the result of that first rollback and do not credit again.
  • If you have no record of the original (it never reached you, or you declined it), nothing was debited. Recommended: answer 200 SUCCESS with balance_before == balance_after, store the rollback under its idempotency_key, and refuse any later /bet/debit whose idempotency_key equals this original_transaction_id. A 404 here leaves the operation open on our side for manual review.
  • Reasons you may see: round cancelled (Bingo Bongo refunds cards of a cancelled round), Rollback due to error after bet_debit: ... (a game failed after your debit), manual rollback or an operator note (19flip staff resolving an unknown outcome; the key then looks like manual-rb-<number>).

POST /win/rollback

Undoes a win: takes the credited amount back from the player.

Request body:

{"player_id": "player-123", "amount": 12.0, "currency": "USD", "round_id": 48213, "session_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7b9d10", "original_transaction_id": "9d7f3a10-6b2c-4e8a-8f41-2c6b0e9a7d55", "idempotency_key": "7c1d2b0e8f4a4b6c9e3d5f7a1b2c4d6e8f0a1b3c5d7e9f1a2b4c6d8e0f1a3b5c", "reason": "Rollback due to error after win_credit: ..."}

Fields are the same as for /bet/rollback; original_transaction_id refers to a /win/credit.

Success response (200):

{
  "transaction_id": "e2a8c4f6-0b1d-4c3e-9f5a-7d6b8e0c2a41",
  "balance_before": 110.5,
  "balance_after": 98.5,
  "status": "SUCCESS"
}

Rules:

  • Same lookup and at-most-once rules as /bet/rollback.
  • The player may already have spent the win. Decide by your policy whether the balance may go negative; if you refuse, answer 402.
  • Rare: today only Block Boost and Mister Twister send it, and only when the game fails right after your credit. Bingo Bongo never takes a paid win back.

POST /round/finish

Added in 2.34.0

Flip Jet sends it after every round in which your players had LIVE bets.

Tells you that a shared game round has ended: its crash point, its time and the outcome of each of your players' bets in it. It moves no money: wins are paid with /win/credit and refunds with /bet/rollback as before. Use it for anything that depends on the round result, for example a jackpot drawn by the crash point, and to close bets that were lost (a loss has no callback of its own).

Sent only for rounds in which at least one of your players had a LIVE bet; DEMO bets are never reported. Other casinos' bets are never included.

Request body (one line on the wire; formatted here):

{
  "game_id": "flip-jet",
  "game_round_id": 327859,
  "type": "shared",
  "status": "finished",
  "crash_point": "19.19",
  "started_at": "2026-10-14T05:16:30.412Z",
  "finished_at": "2026-10-14T05:16:46.087Z",
  "bets": [
    {"round_id": 5123, "player_id": "player-123", "session_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7b9d10", "currency": "USD", "amount": 1.5, "result": "won", "multiplier": "2.35", "win": 3.52},
    {"round_id": 5124, "player_id": "player-123", "session_id": "3f6c2a9e-8d41-4b7a-9c55-1e2f0a7b9d10", "currency": "USD", "amount": 1.0, "result": "lost", "multiplier": null, "win": null}
  ],
  "idempotency_key": "flipjet:327859:FINISH"
}
Field Type Description
game_id string "flip-jet".
game_round_id integer Number of the shared game round, the same for all players. Equals game_round_id in the round's debits and credits. Not the round_id of the money callbacks.
type string "shared": a normal round. "golden_flight": a purchased solo round of one player (Golden Flight); crash_point is then the landing and bets has exactly one won entry with amount = the price. Added in 2.73.0.
route string Golden Flight only. Always aurora since 2.83.0 (earlier flights could have solar or horizon). Absent for shared rounds.
price_mult integer Golden Flight only: the price in bets, 20, 50 or 100. Absent for shared rounds.
status string finished: the round ended with a crash. interrupted: the round was stopped by a failure on our side; bets still in flight are refunded.
crash_point string or null Crash multiplier with two decimals, e.g. "1.00", "19.19", "10000.00". null when status is interrupted.
started_at string or null Takeoff time, UTC, ISO 8601 with milliseconds. null if the round was interrupted before takeoff.
finished_at string Crash (or interruption) time, UTC, ISO 8601 with milliseconds.
bets array Your players' bets that played in this round, see below. Never empty.
idempotency_key string flipjet:<game_round_id>:FINISH. The same on every retry. Unique per round, not per casino.

Each element of bets:

Field Type Description
round_id integer The bet's round_id from its /bet/debit, /win/credit and /bet/rollback.
player_id string Your player.
session_id string Session the bet was placed in.
currency string Currency of the bet.
amount number Bet amount.
result string won: cashed out (the /win/credit may still be on its way). lost: not cashed out before the crash. refunded: the round was interrupted and the bet is returned with /bet/rollback (reason ROUND_VOIDED).
multiplier string or null Cash-out multiplier of a won bet, e.g. "2.35"; null otherwise.
win number or null Win of a won bet (= the /win/credit amount); null otherwise.

Bets that never played in the round are not listed: bets cancelled before takeoff, refused bets and late debits. Their money was already returned with /bet/rollback.

Response: any 2xx means "received". The body is not read; {"status": "OK"} is fine.

Rules:

  • Deduplicate on idempotency_key: a retry carries the same body and key. Answer 2xx to a repeat without processing it again.
  • Order is not guaranteed relative to the money callbacks of the same round: /round/finish may arrive before or after the /win/credit of a cash-out made at the last moment.
  • It is sent once per round, about a second after the crash. If you answer 5xx, time out or cannot be reached, it is retried: 3 quick attempts as for money callbacks, then again after 1, 5, 15, 60 minutes and 3, 6, 12 hours. After that it is dropped; use the round history to fill gaps.
  • A 4xx answer is final and is not retried (for example 404 while you have not implemented the endpoint). Nothing else depends on it.
  • The crash point is never sent before the round ends.

Example: one Block Boost round

  1. /balance → {"balance": 100.0, "currency": "USD"}.
  2. /bet/debit amount 1.0 → balance_after 99.0.
  3. /balance and /bet/debit again for each further paid action in the same round (same round_id, a new idempotency_key each time).
  4. At the end of the round, /win/credit with the total win if it is greater than 0.

Example: one Bingo Bongo round

  1. /balance when the player opens the lobby.
  2. /bet/debit per card purchase (key bb-tkt-...); tips to the host are also debits with a bb-tkt-... key.
  3. After the draw, from a background process: /win/credit for the bank prize (bb-bank-...), jackpot tiers (bb-jpn-..., bb-jpx-...), insurance (bb-ins-...) and host tips (bb-tip-..., credited to the host's player_id).
  4. If the round is cancelled: /bet/rollback per purchase (bb-rfnd-..., reason round cancelled).

Example: one Flip Jet round

  1. /bet/debit per bet (up to two per player per round), each with its own round_id and the shared game_round_id.
  2. /win/credit per cashed-out bet with multiplier.
  3. After the crash: one /round/finish with crash_point and all of your players' bets in the round, lost ones included.

See Game specifics for all per-game differences.