BGaming Integration Guide

Panduan Integrasi BGaming

Use this guide to connect an operator to the backend: manage player balances with a transfer or seamless wallet, then launch and run games for your players.

Gunakan panduan ini untuk menghubungkan operator ke backend: kelola saldo pemain dengan transfer atau seamless wallet, lalu launch dan jalankan game untuk pemain Anda.

Overview

Gambaran Umum

The wallet API authenticates an operator, resolves its stored wallet type, validates the player and currency, and records financial activity in a provider-neutral ledger.

Wallet API mengautentikasi operator, membaca tipe wallet yang tersimpan, memvalidasi pemain dan mata uang, lalu mencatat aktivitas finansial pada ledger yang netral terhadap provider.

Transfer wallet is used when this backend owns the playable balance. Use deposit and withdraw to move funds, and use balance, rollback, and transaction history for operations and audit.

Transfer wallet digunakan ketika backend ini menjadi pemilik saldo bermain. Gunakan deposit dan withdraw untuk memindahkan dana, serta balance, rollback, dan riwayat transaksi untuk operasi dan audit.

Seamless wallet is used when the operator keeps the authoritative balance. The backend sends signed requests to the operator's wallet service for balance, debit, credit, rollback, and transaction-status checks.

Seamless wallet digunakan ketika operator tetap menjadi pemilik saldo utama. Backend mengirim request bertanda tangan ke layanan wallet operator untuk balance, debit, credit, rollback, dan pemeriksaan status transaksi.

Authentication

Autentikasi

All public /api/v1/wallet/* routes require the active operator API token as a bearer token. POST requests also require JSON content type.

Semua route publik /api/v1/wallet/* memerlukan API token operator aktif sebagai bearer token. Request POST juga wajib menggunakan content type JSON.

If the operator has an IP allowlist, the request must come from one of those exact IP addresses. Proxy headers are trusted only when the backend is configured with trusted proxy CIDRs.

Jika operator memiliki daftar IP yang diizinkan, request harus berasal dari salah satu IP tersebut. Header proxy hanya dipercaya jika backend dikonfigurasi dengan CIDR proxy tepercaya.

Public wallet requests are not HMAC-signed. HMAC signing applies only to outbound seamless wallet callbacks.

Request wallet publik tidak menggunakan HMAC. HMAC hanya digunakan untuk callback seamless wallet yang dikirim keluar oleh backend.

Required public API headersHeader wajib API publik
Authorization: Bearer <operator_api_token>
Content-Type: application/json
X-Request-ID: request-unique-id

X-Request-ID is optional. When omitted or invalid, the backend creates one and returns it in the response header.

X-Request-ID bersifat opsional. Jika tidak dikirim atau tidak valid, backend akan membuatnya dan mengembalikannya pada header respons.

Common Response Format

Format Respons Umum

Every application outcome uses HTTP 200 OK. Always check the JSON status and code. Success contains data only; failure contains error only.

Semua hasil aplikasi menggunakan HTTP 200 OK. Selalu periksa status dan code pada JSON. Respons sukses hanya memiliki data; respons gagal hanya memiliki error.

SuccessSukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {}
}
Error
{
  "status": false,
  "code": "VALIDATION_ERROR",
  "error": {}
}

Decimal Money Rules

Aturan Nominal Desimal

Money is decimal 1:1 with a maximum of two decimal places. Do not convert to cents, sen, or other minor units. Amounts must be greater than zero and no greater than 1000000000000.00.

Nominal uang menggunakan desimal 1:1 dengan maksimal dua angka di belakang koma. Jangan mengubah nominal menjadi cents, sen, atau minor unit lainnya. Amount harus lebih besar dari nol dan tidak lebih dari 1000000000000.00.

  • IDR 100000.00 is sent as "100000.00".
  • USD 1.00 is sent as "1.00".
  • "1.001", zero, and negative values are rejected.
  • IDR 100000.00 dikirim sebagai "100000.00".
  • USD 1.00 dikirim sebagai "1.00".
  • "1.001", nol, dan nilai negatif akan ditolak.

The current API serializes decimal response fields as JSON strings and may omit trailing zeros, for example "100000". Treat that value as the same decimal amount as 100000.00.

API saat ini menulis field desimal pada respons sebagai string JSON dan dapat menghilangkan nol di belakang, misalnya "100000". Nilai tersebut tetap sama dengan desimal 100000.00.

Idempotency

reference_id is the idempotency key for debit, deposit, and withdraw. rollback_reference_id is the idempotency key for rollback. Keys are unique per operator.

reference_id adalah idempotency key untuk debit, deposit, dan withdraw. rollback_reference_id adalah idempotency key untuk rollback. Key harus unik per operator.

For seamless callbacks, request_id is a per-call correlation UUID generated by the backend. It can change between calls and must not be used as the financial idempotency key. Deduplicate mutations by reference_id.

Pada callback seamless, request_id adalah UUID korelasi per pemanggilan yang dibuat oleh backend. Nilainya dapat berubah antar pemanggilan dan tidak boleh digunakan sebagai idempotency key finansial. Deduplicate mutasi berdasarkan reference_id.

  • Retry the same financial intent with the same reference and identical fields.
  • An identical completed duplicate returns the original successful result without changing the balance again.
  • Reusing a reference with different user, amount, currency, or operation returns IDEMPOTENCY_CONFLICT.
  • After TRANSACTION_STATUS_UNKNOWN, never create a new reference for the same intent.
  • Ulangi transaksi yang sama dengan reference dan field yang sama.
  • Duplikat identik yang sudah selesai mengembalikan hasil awal tanpa mengubah saldo lagi.
  • Penggunaan ulang reference dengan user, amount, currency, atau operasi berbeda menghasilkan IDEMPOTENCY_CONFLICT.
  • Setelah TRANSACTION_STATUS_UNKNOWN, jangan membuat reference baru untuk transaksi yang sama.

Games: Hosts & Base URLs

Game: Host & Base URL

Game delivery uses three hosts served by one backend. The operator API (player and launch) lives on the API host. The launch URL opens on the game host, and the game loads static assets from the CDN host.

Pengiriman game memakai tiga host yang dilayani satu backend. API operator (pemain dan launch) berada di host API. Launch URL terbuka di host game, dan game memuat aset statis dari host CDN.

Replace these example hosts with the base URLs assigned to your environment.

Ganti host contoh ini dengan base URL yang diberikan untuk environment Anda.

HostsHost
Purpose       Host                              Used for
API           api-bgaming.ohmybet.online        Player + launch + gameplay API
Game shell    bgaming.ohmybet.online            Opens the launch_url in a browser
CDN assets    cdn-bgaming.ohmybet.online        Game loader, bundle, textures, fonts
Tujuan        Host                              Dipakai untuk
API           api-bgaming.ohmybet.online        API pemain + launch + gameplay
Shell game    bgaming.ohmybet.online            Membuka launch_url di browser
Aset CDN      cdn-bgaming.ohmybet.online        Loader game, bundle, tekstur, font
https://api-bgaming.ohmybet.online/api/v1

Create a Player

Buat Pemain

A player is owned by the operator and identified by a stable external_user_id. Create the player once before launching a game. The player currency must be an exact three-letter uppercase code.

Pemain dimiliki oleh operator dan diidentifikasi oleh external_user_id yang stabil. Buat pemain sekali sebelum launch game. Currency pemain harus kode tiga huruf kapital persis.

POST/api/v1/users

Headers: Authorization: Bearer <operator_api_token>, Content-Type: application/json

Request fields

Field request

Field             Type    Requirement  Description                              Example
operator_id       string  Required     Must equal the authenticated operator.   93dc...934c5f
external_user_id  string  Required     Stable player ID owned by operator.      namaakun
username          string  Optional     Display name.                            Akun Satu
currency          string  Required     Exact uppercase 3-letter currency.       USD
Field             Tipe    Kebutuhan  Deskripsi                                   Contoh
operator_id       string  Wajib      Harus sama dengan operator terautentikasi.  93dc...934c5f
external_user_id  string  Wajib      ID pemain stabil milik operator.            namaakun
username          string  Opsional   Nama tampilan.                              Akun Satu
currency          string  Wajib      Mata uang 3 huruf kapital persis.           USD

Notes: Reusing the same external_user_id returns the existing player. New players start with a zero balance; fund a transfer-wallet player with the deposit endpoint.

Catatan: Memakai ulang external_user_id yang sama mengembalikan pemain yang ada. Pemain baru bersaldo nol; isi saldo pemain transfer wallet dengan endpoint deposit.

Request bodyBody request
{
  "operator_id": "93dc0dc4-aa54-40f7-9efe-e8ecf4934c5f",
  "external_user_id": "namaakun",
  "currency": "USD"
}
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "id": "339b0990-79ed-4084-be06-2c49806d0f9f",
    "operator_id": "93dc0dc4-aa54-40f7-9efe-e8ecf4934c5f",
    "external_user_id": "namaakun",
    "currency": "USD",
    "balance_amount": "0",
    "status": "active"
  }
}

Launch a Game

Launch Game

Request a one-time launch URL for a player and a game. Open the returned launch_url in the player's browser to start the session. The launch token is single-use and short-lived; request a fresh launch for each play session.

Minta launch URL sekali pakai untuk seorang pemain dan sebuah game. Buka launch_url yang dikembalikan di browser pemain untuk memulai sesi. Launch token bersifat sekali pakai dan berumur pendek; minta launch baru untuk setiap sesi bermain.

POST/api/v1/game/launch

Headers: Authorization: Bearer <operator_api_token>, Content-Type: application/json

Request fields

Field request

Field             Type    Requirement  Description                              Example
game_code         string  Required     Canonical game code.                     BonanzaTrillion
external_user_id  string  Required     Active player external ID.               namaakun
currency          string  Required     Exact player currency.                   USD
Field             Tipe    Kebutuhan  Deskripsi                                  Contoh
game_code         string  Wajib      Kode game kanonik.                         BonanzaTrillion
external_user_id  string  Wajib      External ID pemain aktif.                  namaakun
currency          string  Wajib      Currency pemain persis.                    USD

Notes: The player and a currency configuration for the game must exist. Unknown game returns GAME_NOT_FOUND; an unconfigured currency returns CURRENCY_NOT_CONFIGURED.

Catatan: Pemain dan konfigurasi currency untuk game harus ada. Game tak dikenal menghasilkan GAME_NOT_FOUND; currency yang belum dikonfigurasi menghasilkan CURRENCY_NOT_CONFIGURED.

Request bodyBody request
{
  "game_code": "BonanzaTrillion",
  "external_user_id": "namaakun",
  "currency": "USD"
}
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "launch_url": "https://bgaming.ohmybet.online/games/BonanzaTrillion/USD?launch_token=...",
    "game_code": "BonanzaTrillion",
    "game_type": "slot",
    "currency": "USD"
  }
}
curl
BASE_URL="https://api-bgaming.ohmybet.online"
TOKEN="op_live_b4fdec69a4d16b8c62f6af178a3eb68ae0c6bb553f553603"
OPERATOR_ID="93dc0dc4-aa54-40f7-9efe-e8ecf4934c5f"

curl -sS -X POST "$BASE_URL/api/v1/game/launch" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"game_code\":\"BonanzaTrillion\",\"external_user_id\":\"namaakun\",\"currency\":\"USD\"}"

Gameplay Protocol

Protokol Gameplay

After the launch URL opens, the game client talks to the gameplay endpoint directly. This is the native game protocol, not the operator wallet API. As an operator you do not call it; it is documented here for reference.

Setelah launch URL terbuka, client game berkomunikasi langsung ke endpoint gameplay. Ini protokol game native, bukan wallet API operator. Sebagai operator Anda tidak memanggilnya; didokumentasikan di sini sebagai referensi.

POST/api/{game_code}/{casino_id}/{play_token}

The player session is authenticated by the play_token in the URL path. A CSRF token is verified only if the client sends the X-CSRF-Token header.

Sesi pemain diautentikasi oleh play_token di path URL. CSRF token diverifikasi hanya jika client mengirim header X-CSRF-Token.

Commands include init (load options and balance), spin (place a bet), and freespin (play a free spin). Every response uses HTTP 200 and the native game shape with api_version, the command payload, balance, and flow.

Command meliputi init (muat opsi dan saldo), spin (pasang taruhan), dan freespin (mainkan free spin). Setiap respons memakai HTTP 200 dan bentuk game native dengan api_version, payload command, balance, dan flow.

init requestRequest init
POST /api/BonanzaTrillion/3035793/<play_token>
X-CSRF-Token: <csrf_token>
Content-Type: application/json

{ "command": "init" }
init response (shortened)Respons init (dipersingkat)
{
  "api_version": "2",
  "options": {
    "available_bets": [20, 40, 60, 100, 200],
    "default_bet": 100,
    "currency": { "code": "USD", "symbol": "$", "subunits": 100, "exponent": 2 }
  },
  "balance": { "game": 0, "wallet": 100000 },
  "flow": { "state": "ready", "command": "init", "available_actions": ["init", "spin"] }
}
spin requestRequest spin
{ "command": "spin", "options": { "bet": 100 } }

Supported Games

Game Tersedia

Each game is identified by its canonical game_code and exposes a per-currency bet configuration. Long-run payout follows the player or operator RTP setting.

Setiap game diidentifikasi oleh game_code kanonik dan punya konfigurasi taruhan per-currency. Payout jangka panjang mengikuti pengaturan RTP pemain atau operator.

game_code        Type     Description
BonanzaTrillion  slot     Cascade slot with tumbling pays, scatter free spins, buy feature, and x2-x500 multipliers.
Aviamasters      casual   Flight game; the plane collects multipliers and must land to win, or crashes into the sea.
game_code        Tipe     Deskripsi
BonanzaTrillion  slot     Slot cascade dengan tumbling pays, free spins scatter, beli fitur, dan multiplier x2-x500.
Aviamasters      casual   Game penerbangan; pesawat mengumpulkan multiplier dan harus mendarat untuk menang, atau jatuh ke laut.

Live Trial

Coba Langsung

Launch either game against the live environment using the demo operator and player below. Each click requests a fresh launch URL and opens the game in a new tab. The demo credentials are intentionally public for trial use.

Launch salah satu game ke environment live memakai operator dan pemain demo di bawah. Setiap klik meminta launch URL baru dan membuka game di tab baru. Kredensial demo ini sengaja dibuat publik untuk keperluan uji coba.

API host          https://api-bgaming.ohmybet.online
Operator ID       93dc0dc4-aa54-40f7-9efe-e8ecf4934c5f
Player            namaakun
Currency          USD
Host API          https://api-bgaming.ohmybet.online
Operator ID       93dc0dc4-aa54-40f7-9efe-e8ecf4934c5f
Pemain            namaakun
Currency          USD

Bonanza Trillion

slot

Cascade slot with free spins and buy feature.

Slot cascade dengan free spins dan beli fitur.

Aviamasters

casual

Flight game; land the plane to win.

Game penerbangan; daratkan pesawat untuk menang.


Transfer Wallet

Transfer mode stores the authoritative playable balance in this backend. A common integration flow is: check balance, deposit funds, withdraw funds when required, and use rollback only to reverse one completed transaction.

Mode transfer menyimpan saldo bermain utama di backend ini. Alur integrasi umumnya: periksa saldo, lakukan deposit, lakukan withdraw bila diperlukan, dan gunakan rollback hanya untuk membalik satu transaksi yang sudah selesai.

https://api-bgaming.ohmybet.online/api/v1

Replace the example host with the base URL assigned to your environment.

Ganti host contoh dengan base URL yang diberikan untuk environment Anda.

Balance

Returns the current local balance for an active transfer-wallet player. This endpoint does not create a ledger transaction.

Mengembalikan saldo lokal saat ini untuk pemain transfer wallet yang aktif. Endpoint ini tidak membuat transaksi ledger.

GET/api/v1/wallet/balance

Headers: Authorization: Bearer <operator_api_token>

Request body: None. Send the fields as query parameters.

Body request: Tidak ada. Kirim field sebagai query parameter.

Query fields

Field query

Field             Type    Requirement  Description                         Example
external_user_id  string  Required     Player ID owned by the operator.    player-1001
currency          string  Required     Exact uppercase 3-letter currency.  IDR
Field             Tipe    Kebutuhan  Deskripsi                            Contoh
external_user_id  string  Wajib      ID pemain milik operator.            player-1001
currency          string  Wajib      Mata uang 3 huruf kapital persis.    IDR

Notes: Currency must match the player currency. Unknown users return USER_NOT_FOUND.

Catatan: Currency harus sama dengan currency pemain. User yang tidak ditemukan menghasilkan USER_NOT_FOUND.

RequestRequest
GET /api/v1/wallet/balance?external_user_id=player-1001&currency=IDR
Authorization: Bearer <operator_api_token>
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "balance_amount": "100000",
    "currency": "IDR",
    "timestamp": "2026-06-12T00:00:00Z"
  }
}
Error responseRespons error
{
  "status": false,
  "code": "CURRENCY_MISMATCH",
  "error": {}
}

Deposit

Credits the transfer-wallet player's local balance and writes one completed ledger row.

Menambah saldo lokal pemain transfer wallet dan menulis satu row ledger berstatus completed.

POST/api/v1/wallet/deposit

Headers: Authorization: Bearer <operator_api_token>, Content-Type: application/json

Request fields

Field request

Field             Type     Requirement  Description                              Example
operator_id       string   Required     Must equal the authenticated operator.   7fe0...2b1
external_user_id  string   Required     Active player external ID.               player-1001
reference_id      string   Required     Unique idempotency key per operator.      deposit-0001
amount            decimal  Required     Positive, max 2 decimals.                100000.00
currency          string   Required     Exact player currency.                   IDR
Field             Tipe     Kebutuhan  Deskripsi                                  Contoh
operator_id       string   Wajib      Harus sama dengan operator terautentikasi. 7fe0...2b1
external_user_id  string   Wajib      External ID pemain aktif.                  player-1001
reference_id      string   Wajib      Idempotency key unik per operator.         deposit-0001
amount            decimal  Wajib      Positif, maksimal 2 desimal.               100000.00
currency          string   Wajib      Harus sama dengan currency pemain.         IDR

Notes: Unknown JSON fields are rejected. A different operator_id returns FORBIDDEN.

Catatan: Field JSON yang tidak dikenal akan ditolak. operator_id yang berbeda menghasilkan FORBIDDEN.

Request bodyBody request
{
  "operator_id": "7fe0c978-3eb2-4e19-bc69-e2cdb74a12b1",
  "external_user_id": "player-1001",
  "reference_id": "deposit-0001",
  "amount": "100000.00",
  "currency": "IDR"
}
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "id": "c2ea59c1-d143-46d1-bff4-6e302038849b",
    "operator_id": "7fe0c978-3eb2-4e19-bc69-e2cdb74a12b1",
    "user_id": "2ff15b51-a20a-4694-a01a-f1ad77ec34d7",
    "external_user_id": "player-1001",
    "wallet_type": "transfer",
    "type": "credit",
    "amount": "100000",
    "currency": "IDR",
    "balance_before": "0",
    "balance_after": "100000",
    "reference_id": "deposit-0001",
    "status": "completed",
    "failure_code": null,
    "metadata": {},
    "created_at": "2026-06-12T00:00:00Z",
    "completed_at": "2026-06-12T00:00:00Z"
  }
}
Error responseRespons error
{
  "status": false,
  "code": "IDEMPOTENCY_CONFLICT",
  "error": {}
}

Withdraw

Debits the transfer-wallet player's local balance. The operation fails without changing the balance when funds are insufficient.

Mengurangi saldo lokal pemain transfer wallet. Operasi gagal tanpa mengubah saldo jika saldo tidak mencukupi.

POST/api/v1/wallet/withdraw

Headers: Authorization: Bearer <operator_api_token>, Content-Type: application/json

Request fields

Field request

Field             Type     Requirement  Description                              Example
operator_id       string   Required     Must equal the authenticated operator.   7fe0...2b1
external_user_id  string   Required     Active player external ID.               player-1001
reference_id      string   Required     Unique idempotency key per operator.      withdraw-0001
amount            decimal  Required     Positive, max 2 decimals.                25000.00
currency          string   Required     Exact player currency.                   IDR
Field             Tipe     Kebutuhan  Deskripsi                                  Contoh
operator_id       string   Wajib      Harus sama dengan operator terautentikasi. 7fe0...2b1
external_user_id  string   Wajib      External ID pemain aktif.                  player-1001
reference_id      string   Wajib      Idempotency key unik per operator.         withdraw-0001
amount            decimal  Wajib      Positif, maksimal 2 desimal.               25000.00
currency          string   Wajib      Harus sama dengan currency pemain.         IDR

Notes: An insufficient attempt is recorded as a failed debit and returns INSUFFICIENT_BALANCE.

Catatan: Percobaan dengan saldo tidak cukup dicatat sebagai debit gagal dan menghasilkan INSUFFICIENT_BALANCE.

Request bodyBody request
{
  "operator_id": "7fe0c978-3eb2-4e19-bc69-e2cdb74a12b1",
  "external_user_id": "player-1001",
  "reference_id": "withdraw-0001",
  "amount": "25000.00",
  "currency": "IDR"
}
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "id": "52406740-b02d-4ffc-955f-598cc7f013d7",
    "operator_id": "7fe0c978-3eb2-4e19-bc69-e2cdb74a12b1",
    "user_id": "2ff15b51-a20a-4694-a01a-f1ad77ec34d7",
    "external_user_id": "player-1001",
    "wallet_type": "transfer",
    "type": "debit",
    "amount": "25000",
    "currency": "IDR",
    "balance_before": "100000",
    "balance_after": "75000",
    "reference_id": "withdraw-0001",
    "status": "completed",
    "failure_code": null,
    "metadata": {},
    "created_at": "2026-06-12T00:01:00Z",
    "completed_at": "2026-06-12T00:01:00Z"
  }
}
Error responseRespons error
{
  "status": false,
  "code": "INSUFFICIENT_BALANCE",
  "error": {}
}

Rollback

Reverses one completed credit or debit. The amount and currency are loaded from the original ledger row and must not be sent by the client.

Membalik satu credit atau debit yang sudah completed. Amount dan currency dibaca dari ledger transaksi awal dan tidak boleh dikirim oleh client.

POST/api/v1/wallet/rollback

Headers: Authorization: Bearer <operator_api_token>, Content-Type: application/json

Request fields

Field request

Field                  Type    Requirement  Description                              Example
external_user_id       string  Required     Owner of the original transaction.       player-1001
original_reference_id  string  Required     Reference of completed credit/debit.     withdraw-0001
rollback_reference_id  string  Required     Unique idempotency key for rollback.     rollback-0001
Field                  Tipe    Kebutuhan  Deskripsi                                 Contoh
external_user_id       string  Wajib      Pemilik transaksi awal.                  player-1001
original_reference_id  string  Wajib      Reference credit/debit yang completed.   withdraw-0001
rollback_reference_id  string  Wajib      Idempotency key unik untuk rollback.     rollback-0001

Notes: Only one rollback is allowed per original transaction. A completed rollback marks the original transaction as reversed.

Catatan: Hanya satu rollback diperbolehkan untuk setiap transaksi awal. Rollback yang selesai mengubah status transaksi awal menjadi reversed.

Request bodyBody request
{
  "external_user_id": "player-1001",
  "original_reference_id": "withdraw-0001",
  "rollback_reference_id": "rollback-0001"
}
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "transaction_id": "bbd69c4f-7bc6-46d2-b6ce-3bdbf927d75a",
    "balance_after": "99000",
    "currency": "IDR",
    "timestamp": "2026-06-12T00:03:00Z"
  }
}
Error responseRespons error
{
  "status": false,
  "code": "TRANSACTION_ALREADY_ROLLED_BACK",
  "error": {}
}

Transactions

Returns ledger rows owned by the authenticated operator. Results are ordered newest first.

Mengembalikan row ledger milik operator terautentikasi. Hasil diurutkan dari yang terbaru.

GET/api/v1/wallet/transactions

Headers: Authorization: Bearer <operator_api_token>

Request body: None. All filters are query parameters.

Body request: Tidak ada. Semua filter dikirim sebagai query parameter.

Query fields

Field query

Field             Type     Requirement  Description                                      Example
external_user_id  string   Optional     Filter by player external ID.                    player-1001
type              string   Optional     credit, debit, or rollback.                      debit
status            string   Optional     pending, completed, failed, reversed, mismatch.  completed
reference_id      string   Optional     Exact reference filter.                          withdraw-0001
limit             integer  Optional     Default 20; minimum 1; maximum 100.               20
offset            integer  Optional     Default 0; range 0 through 10000.                 0
Field             Tipe     Kebutuhan  Deskripsi                                         Contoh
external_user_id  string   Opsional   Filter berdasarkan external ID pemain.            player-1001
type              string   Opsional   credit, debit, atau rollback.                     debit
status            string   Opsional   pending, completed, failed, reversed, mismatch.   completed
reference_id      string   Opsional   Filter reference persis.                          withdraw-0001
limit             integer  Opsional   Default 20; minimum 1; maksimum 100.               20
offset            integer  Opsional   Default 0; rentang 0 sampai 10000.                 0

Notes: This is ledger history, not a single-purpose transaction-status endpoint.

Catatan: Endpoint ini adalah riwayat ledger, bukan endpoint khusus status satu transaksi.

RequestRequest
GET /api/v1/wallet/transactions?external_user_id=player-1001&limit=20&offset=0
Authorization: Bearer <operator_api_token>
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "items": [
      {
        "id": "52406740-b02d-4ffc-955f-598cc7f013d7",
        "operator_id": "7fe0c978-3eb2-4e19-bc69-e2cdb74a12b1",
        "user_id": "2ff15b51-a20a-4694-a01a-f1ad77ec34d7",
        "external_user_id": "player-1001",
        "wallet_type": "transfer",
        "type": "debit",
        "amount": "25000",
        "currency": "IDR",
        "balance_before": "100000",
        "balance_after": "75000",
        "reference_id": "withdraw-0001",
        "status": "completed",
        "failure_code": null,
        "metadata": {},
        "created_at": "2026-06-12T00:01:00Z",
        "completed_at": "2026-06-12T00:01:00Z"
      }
    ],
    "limit": 20,
    "offset": 0
  }
}
Error responseRespons error
{
  "status": false,
  "code": "INVALID_TRANSACTION_STATUS",
  "error": {}
}

Testing Transfer Wallet

Pengujian Transfer Wallet

  1. Start with a player balance of 0.00.
  2. Deposit 100000.00. Expected balance: 100000.00.
  3. Withdraw 25000.00. Expected balance: 75000.00.
  4. Rollback the withdraw. Expected balance: 100000.00.
  5. Repeat the same request with the same reference. The balance must not change twice.
  1. Mulai dengan saldo pemain 0.00.
  2. Deposit 100000.00. Saldo yang diharapkan: 100000.00.
  3. Withdraw 25000.00. Saldo yang diharapkan: 75000.00.
  4. Rollback withdraw. Saldo yang diharapkan: 100000.00.
  5. Ulangi request yang sama dengan reference yang sama. Saldo tidak boleh berubah dua kali.
curl
BASE_URL="https://api.example.com"
TOKEN="replace-with-operator-token"
OPERATOR_ID="7fe0c978-3eb2-4e19-bc69-e2cdb74a12b1"

curl -sS -X POST "$BASE_URL/api/v1/wallet/deposit" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"operator_id\":\"$OPERATOR_ID\",\"external_user_id\":\"player-1001\",\"reference_id\":\"test-deposit-1\",\"amount\":\"100000.00\",\"currency\":\"IDR\"}"

curl -sS -X POST "$BASE_URL/api/v1/wallet/withdraw" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"operator_id\":\"$OPERATOR_ID\",\"external_user_id\":\"player-1001\",\"reference_id\":\"test-withdraw-1\",\"amount\":\"25000.00\",\"currency\":\"IDR\"}"

curl -sS -X POST "$BASE_URL/api/v1/wallet/rollback" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"external_user_id":"player-1001","original_reference_id":"test-withdraw-1","rollback_reference_id":"test-rollback-1"}'

curl -sS "$BASE_URL/api/v1/wallet/balance?external_user_id=player-1001&currency=IDR" \
  -H "Authorization: Bearer $TOKEN"

Known Error Codes

Kode Error yang Dikenal

The codes below are returned by the public wallet handlers or accepted from the seamless operator wallet service.

Kode berikut dikembalikan oleh handler wallet publik atau diterima dari layanan seamless wallet operator.

Code                                 Meaning
SUCCESS                              Request completed successfully.
VALIDATION_ERROR                     Missing field, invalid JSON, wrong content type, or unknown field.
UNAUTHORIZED                         Bearer token is missing, invalid, expired, revoked, or operator is inactive.
FORBIDDEN                            Client IP or request operator identity is not allowed.
RATE_LIMITED                         Request rate limit was exceeded.
USER_NOT_FOUND                       Player was not found for the authenticated operator.
USER_INACTIVE                        Player status is not active.
INVALID_CURRENCY                     Currency is not exactly three uppercase ASCII letters.
CURRENCY_MISMATCH                    Request currency differs from player or upstream currency.
INVALID_AMOUNT                       Amount is zero, negative, or has more than two decimal places.
AMOUNT_LIMIT_EXCEEDED                Amount is greater than 1000000000000.00.
BALANCE_OVERFLOW                     Balance mutation failed its safety checks.
INSUFFICIENT_BALANCE                 Wallet cannot cover the debit.
WALLET_TYPE_NOT_SUPPORTED            Route cannot run for the authenticated wallet type.
IDEMPOTENCY_CONFLICT                 Reference was reused with different immutable fields.
TRANSACTION_NOT_FOUND                Original transaction or requested transaction does not exist.
TRANSACTION_NOT_ROLLBACKABLE         Original transaction is not completed or cannot be reversed.
TRANSACTION_ALREADY_ROLLED_BACK      Original transaction was already reversed.
INVALID_TRANSACTION_TYPE             Transaction list type filter is invalid.
INVALID_TRANSACTION_STATUS           Transaction list status filter is invalid.
INVALID_PAGINATION                   limit or offset is outside the accepted range.
PROVIDER_UNAVAILABLE                 Seamless wallet provider is unavailable.
UPSTREAM_TIMEOUT                     Seamless balance or status request timed out.
TRANSACTION_STATUS_UNKNOWN           Mutation result is uncertain and requires reconciliation.
UNSUPPORTED_OPERATION                Wallet strategy does not support the requested operation.
INTERNAL_ERROR                       Unexpected internal or configuration failure.
Code                                 Arti
SUCCESS                              Request berhasil.
VALIDATION_ERROR                     Field kurang, JSON invalid, content type salah, atau field tidak dikenal.
UNAUTHORIZED                         Bearer token tidak ada/tidak valid, credential kedaluwarsa/dicabut, atau operator tidak aktif.
FORBIDDEN                            IP client atau identitas operator pada request tidak diizinkan.
RATE_LIMITED                         Batas jumlah request terlampaui.
USER_NOT_FOUND                       Pemain tidak ditemukan untuk operator terautentikasi.
USER_INACTIVE                        Status pemain tidak aktif.
INVALID_CURRENCY                     Currency bukan tiga huruf ASCII kapital.
CURRENCY_MISMATCH                    Currency request berbeda dari pemain atau upstream.
INVALID_AMOUNT                       Amount nol, negatif, atau lebih dari dua desimal.
AMOUNT_LIMIT_EXCEEDED                Amount lebih besar dari 1000000000000.00.
BALANCE_OVERFLOW                     Mutasi saldo gagal pada pemeriksaan keamanan.
INSUFFICIENT_BALANCE                 Saldo tidak cukup untuk debit.
WALLET_TYPE_NOT_SUPPORTED            Route tidak dapat dijalankan untuk tipe wallet operator.
IDEMPOTENCY_CONFLICT                 Reference digunakan ulang dengan field penting yang berbeda.
TRANSACTION_NOT_FOUND                Transaksi awal atau transaksi yang diminta tidak ditemukan.
TRANSACTION_NOT_ROLLBACKABLE         Transaksi awal belum completed atau tidak dapat dibalik.
TRANSACTION_ALREADY_ROLLED_BACK      Transaksi awal sudah dibalik.
INVALID_TRANSACTION_TYPE             Filter type pada daftar transaksi tidak valid.
INVALID_TRANSACTION_STATUS           Filter status pada daftar transaksi tidak valid.
INVALID_PAGINATION                   limit atau offset di luar rentang.
PROVIDER_UNAVAILABLE                 Provider seamless wallet tidak tersedia.
UPSTREAM_TIMEOUT                     Request balance atau status seamless mengalami timeout.
TRANSACTION_STATUS_UNKNOWN           Hasil mutasi belum pasti dan memerlukan rekonsiliasi.
UNSUPPORTED_OPERATION                Strategi wallet tidak mendukung operasi.
INTERNAL_ERROR                       Kegagalan internal atau konfigurasi yang tidak terduga.

Seamless upstream code mapping

Pemetaan kode upstream seamless

The operator wallet may also return DUPLICATE_TRANSACTION (mapped to IDEMPOTENCY_CONFLICT), OPERATOR_SUSPENDED (mapped to provider unavailable), and INVALID_SIGNATURE or INVALID_TIMESTAMP (mapped to INTERNAL_ERROR). A mutation response with INTERNAL_ERROR is treated as an unknown outcome.

Wallet operator juga dapat mengembalikan DUPLICATE_TRANSACTION (dipetakan ke IDEMPOTENCY_CONFLICT), OPERATOR_SUSPENDED (dipetakan ke provider unavailable), serta INVALID_SIGNATURE atau INVALID_TIMESTAMP (dipetakan ke INTERNAL_ERROR). Respons mutasi dengan INTERNAL_ERROR dianggap sebagai hasil belum pasti.