Crabby's Gold Integration Guide

Panduan Integrasi Crabby's Gold

Connect an operator, create players, launch Crabby's Gold, and integrate either a transfer or seamless wallet using the production API contract.

Hubungkan operator, buat pemain, launch Crabby's Gold, dan integrasikan transfer atau seamless wallet menggunakan kontrak API production.

Overview

Gambaran Umum

The authenticated operator determines wallet mode, currency, player ownership, and access. Game launch and wallet endpoints never accept a client-selected wallet strategy.

Operator yang terautentikasi menentukan mode wallet, currency, kepemilikan pemain, dan akses. Endpoint game launch dan wallet tidak menerima strategi wallet pilihan client.

Transfer wallet: this backend owns the playable balance and updates it atomically in PostgreSQL alongside a per-round ledger.

Transfer wallet: backend ini menjadi pemilik saldo bermain dan memperbaruinya secara atomik di PostgreSQL bersama ledger per-round.

Seamless wallet: the operator owns the balance. This backend sends signed callbacks to the operator's configured callback URL on every debit, credit, and rollback.

Seamless wallet: operator menjadi pemilik saldo. Backend ini mengirim callback bertanda tangan ke callback URL operator setiap debit, credit, dan rollback.

Gameplay: after launch, the Play'n GO game client connects on its own to wss://playngo.ohmybet.online/gamehub and runs the spins — you do not call any gameplay endpoint. Each spin settles the bet and win through the same wallet gateway.

Gameplay: setelah launch, client game Play'n GO terhubung sendiri ke wss://playngo.ohmybet.online/gamehub dan menjalankan spin — Anda tidak memanggil endpoint gameplay apa pun. Setiap spin menyelesaikan bet dan win melalui wallet gateway yang sama.

Authentication

Autentikasi

All operator endpoints under /api/v1/users, /api/v1/game, and /api/v1/wallet require an active API token as a bearer token.

Semua endpoint operator di bawah /api/v1/users, /api/v1/game, dan /api/v1/wallet memerlukan API token aktif sebagai bearer token.

Keep API tokens and secret keys on a trusted server. Never embed them in browser JavaScript, mobile applications, launch URLs, logs, or public repositories.

Simpan API token dan secret key di server tepercaya. Jangan menaruhnya di JavaScript browser, aplikasi mobile, launch URL, log, atau repository publik.

If an operator has an IP allowlist, requests must arrive from an allowed address. X-Request-ID is optional and is returned in the response headers.

Jika operator memiliki IP allowlist, request harus berasal dari alamat yang diizinkan. X-Request-ID opsional dan dikembalikan pada header respons.

Gameplay needs no operator token from you: the game client opens the WebSocket itself using the one-time ticket embedded in the launch_url issued by POST /api/v1/game/launch.

Gameplay tidak memerlukan token operator dari Anda: client game membuka WebSocket sendiri memakai ticket sekali-pakai di dalam launch_url yang diterbitkan oleh POST /api/v1/game/launch.

Required headersHeader wajib
Authorization: Bearer <operator_api_token>
Content-Type: application/json
X-Request-ID: 6ca6a797-a9d6-4b16-ae51-f4184472ba23
Launch URL format (issued by launch)Format launch URL (dari launch)
https://playngo.ohmybet.online/casino/ContainerLauncher?channel=desktop&gameid=920&lang=en_US&pid=8842&practice=0&ticket=<session_ticket>

Common Response Format

Format Respons Umum

Every operator API outcome uses HTTP 200 OK. Read the JSON status and code; do not infer the result from HTTP status.

Semua hasil operator API menggunakan HTTP 200 OK. Baca status dan code pada JSON; jangan menentukan hasil dari HTTP status.

A success response has data and no error. An error response has error and no data.

Respons sukses memiliki data tanpa error. Respons gagal memiliki error tanpa data.

Gameplay runs over the game-client WebSocket, not the REST API; see Gameplay Protocol.

Gameplay berjalan lewat WebSocket client game, bukan REST API; lihat Protokol Gameplay.

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

Integer Money Rules

Aturan Nominal Integer

All wallet amounts are signed 64-bit JSON integers in minor units. Never send floating-point values or numeric strings. A mutation amount must be from 1 through 1000000000000.

Semua nominal wallet adalah integer JSON 64-bit dalam minor unit. Jangan mengirim floating-point atau string angka. Amount mutasi harus dari 1 sampai 1000000000000.

  • IDR: denominator is 1 (whole rupiah, NOT cents). 100000000 minor units means Rp 100,000,000.
  • USD: denominator is 100. 10000 minor units means USD 100.00.
  • Currency: exactly three uppercase ASCII letters and must match the player.
  • Overflow: credits that exceed integer balance capacity are rejected with BALANCE_OVERFLOW.
  • Gameplay: bets and wins are integers in the currency's smallest unit, settled in the same unit as balance_amount.
  • IDR: denominator 1 (rupiah utuh, BUKAN sen). 100000000 minor unit berarti Rp 100.000.000.
  • USD: denominator 100. 10000 minor unit berarti USD 100.00.
  • Currency: tepat tiga huruf ASCII kapital dan harus sama dengan currency pemain.
  • Overflow: credit yang membuat saldo melewati kapasitas integer ditolak dengan BALANCE_OVERFLOW.
  • Gameplay: taruhan dan kemenangan adalah integer dalam unit terkecil mata uang, diselesaikan dalam unit yang sama dengan balance_amount.

Idempotency

reference_id is the operator-scoped idempotency key for debit, credit, deposit, and withdraw. rollback_reference_id is the key for rollback. The game engine reflects every spin as a debit (bet) and an optional credit (win) in the same wallet ledger, using a round-scoped reference.

reference_id adalah idempotency key per operator untuk debit, credit, deposit, dan withdraw. rollback_reference_id adalah key untuk rollback. Game engine mencatat setiap spin sebagai debit (bet) dan opsional credit (win) di ledger wallet yang sama, dengan reference per-round.

  • Retry an identical request with the same key to receive the original result.
  • Reusing a key with different user, amount, currency, or operation returns IDEMPOTENCY_CONFLICT.
  • One completed transaction can be rolled back only once.
  • A seamless mutation timeout is an unknown outcome. Reconcile it before retrying.
  • Retry request identik dengan key yang sama untuk menerima hasil awal.
  • Memakai ulang key dengan user, amount, currency, atau operasi berbeda menghasilkan IDEMPOTENCY_CONFLICT.
  • Satu transaksi completed hanya dapat di-rollback satu kali.
  • Timeout mutasi seamless adalah hasil yang belum diketahui. Lakukan rekonsiliasi sebelum retry.

Games: Hosts & Base URLs

Game: Host & Base URL

One production host serves the operator API, the launch URL, the game client, its static assets, and the gameplay WebSocket. The documentation host serves this guide and its trial launcher.

Satu host production melayani operator API, launch URL, client game, aset statis, dan WebSocket gameplay. Host dokumentasi melayani panduan ini dan trial launcher.

Production hostsHost production
Operator API     https://playngo.ohmybet.online/api/v1
Game launch      https://playngo.ohmybet.online/casino/ContainerLauncher
Game client/WS   wss://playngo.ohmybet.online/gamehub  (driven by the game client)
Documentation    https://playngo-docs.pages.dev
https://playngo.ohmybet.online/api/v1

Create a Player

Buat Pemain

Create the player before launch. external_user_id is the stable identity within one operator. A duplicate identity returns USER_ALREADY_EXISTS. The new player starts with a zero balance; fund it with a deposit (transfer mode).

Buat pemain sebelum launch. external_user_id adalah identitas stabil di dalam satu operator. Identitas duplikat menghasilkan USER_ALREADY_EXISTS. Pemain baru memiliki saldo nol; isi dengan deposit (mode transfer).

POST/api/v1/users

Request fields

Field request

operator_id      string  required  Authenticated operator UUID
external_user_id string  required  Stable player identity
username         string  optional  Display name
currency         string  required  Exact uppercase currency
operator_id      string  wajib     UUID operator terautentikasi
external_user_id string  wajib     Identitas pemain yang stabil
username         string  opsional  Nama tampilan
currency         string  wajib     Currency kapital yang tepat
Request bodyBody request
{
  "operator_id": "<operator_uuid>",
  "external_user_id": "player001",
  "username": "Player 001",
  "currency": "USD"
}
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "id": "a9375dd8-0973-4bbf-9cf0-cd60f50b2414",
    "operator_id": "<operator_uuid>",
    "external_user_id": "player001",
    "username": "Player 001",
    "currency": "USD",
    "balance_amount": 0,
    "rtp": null,
    "status": "active",
    "created_at": "2026-06-21T12:00:00Z",
    "updated_at": "2026-06-21T12:00:00Z"
  }
}

Launch a Game

Launch Game

Request a fresh launch URL from your trusted backend, then redirect or open it in the player's browser. The launch session is idle-based and slides on each activity; it expires after 30 minutes of inactivity by default.

Minta launch URL baru dari backend tepercaya Anda, lalu redirect atau buka URL tersebut di browser pemain. Sesi launch berbasis idle dan diperpanjang setiap ada aktivitas; default berakhir setelah 30 menit tanpa aktivitas.

POST/api/v1/game/launch

Request fields

Field request

game_id          string  required  Canonical game id, "crabbysgold"
external_user_id string  required  Existing active player
language         string  optional  Letters, '-' or '_'; defaults to "en"
game_id          string  wajib     Game id kanonik, "crabbysgold"
external_user_id string  wajib     Pemain aktif yang sudah ada
language         string  opsional  Huruf, '-' atau '_'; default "en"

An unknown game id returns NOT_FOUND. A missing player id returns VALIDATION_ERROR. A player that does not exist returns USER_NOT_FOUND. An inactive player returns USER_INACTIVE.

Game id tidak dikenal menghasilkan NOT_FOUND. Player id kosong menghasilkan VALIDATION_ERROR. Pemain tidak ada menghasilkan USER_NOT_FOUND. Pemain non-aktif menghasilkan USER_INACTIVE.

Request bodyBody request
{
  "game_id": "crabbysgold",
  "external_user_id": "player001",
  "language": "en"
}
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "launch_url": "https://playngo.ohmybet.online/casino/ContainerLauncher?channel=desktop&gameid=920&lang=en_US&pid=8842&practice=0&ticket=45d01eea2bebed6ca1afa4d00a250182e43a26f0cf653ba1"
  }
}
curl
curl -sS https://playngo.ohmybet.online/api/v1/game/launch \
  -H "Authorization: Bearer <operator_api_token>" \
  -H "Content-Type: application/json" \
  --data '{
    "game_id":"crabbysgold",
    "external_user_id":"player001",
    "language":"en"
  }'

Gameplay Protocol

Protokol Gameplay

You do not implement any gameplay protocol. After you open the launch_url, the Play'n GO game client loads in the player's browser and connects back to wss://playngo.ohmybet.online/gamehub on its own to run spins. Bets and wins settle automatically against the player's wallet — debited/credited from the transfer balance, or via your seamless callbacks if you use seamless mode.

Anda tidak mengimplementasikan protokol gameplay apa pun. Setelah Anda membuka launch_url, client game Play'n GO dimuat di browser pemain dan terhubung sendiri ke wss://playngo.ohmybet.online/gamehub untuk menjalankan spin. Taruhan dan kemenangan diselesaikan otomatis terhadap wallet pemain — didebit/dikredit dari saldo transfer, atau via callback seamless Anda jika memakai mode seamless.

  • Your backend calls POST /api/v1/game/launch and gets a launch_url.
  • You open that URL (full page or iframe) in the player's browser.
  • The game client plays out spins over the WebSocket; you do nothing further.
  • Track balance/outcomes via the Wallet endpoints or your seamless callbacks.
  • Backend Anda memanggil POST /api/v1/game/launch dan menerima launch_url.
  • Anda membuka URL itu (full page atau iframe) di browser pemain.
  • Client game menjalankan spin lewat WebSocket; Anda tidak melakukan apa-apa lagi.
  • Pantau saldo/hasil via endpoint Wallet atau callback seamless Anda.
Embed the launch URLEmbed launch URL
<iframe
  src="<launch_url>"
  allow="autoplay; fullscreen"
  style="width:100%;height:100vh;border:0">
</iframe>
Game client connection (automatic)Koneksi client game (otomatis)
wss://playngo.ohmybet.online/gamehub

Supported Games

Game Tersedia

The production catalog currently exposes one title, Crabby's Gold (Play'n GO). Pass its game_id to the launch endpoint.

Katalog production saat ini menyediakan satu judul, Crabby's Gold (Play'n GO). Kirim game_id ini ke endpoint launch.

game_id       crabbysgold
name          Crabby's Gold
provider      Play'n GO
layout        6 reels x 4 rows, 4096 ways, max win 70000x
launch path   /casino/ContainerLauncher?...&ticket=<session_ticket>
gameplay      wss://playngo.ohmybet.online/gamehub  (driven by the game client)

Live Trial

Coba Langsung

This demo button calls the operator launch API directly and opens the game in a new tab. Production integrations must keep the operator token server-side instead.

Tombol demo ini memanggil API launch operator secara langsung dan membuka game di tab baru. Integrasi production wajib menyimpan token operator di server.

game        Crabby's Gold
game_id     crabbysgold
player      player001

Crabby's Gold

slot

Production trial using a dedicated transfer-wallet player on the live VPS.

Trial production menggunakan pemain transfer-wallet khusus di VPS live.


Transfer Wallet Flow

Alur Transfer Wallet

Transfer mode stores the authoritative balance locally. Create the player, deposit funds, launch the game, and use the ledger endpoints for audit or controlled corrections. Every spin reflects the bet as a debit row and any win as a credit row in the wallet ledger.

Mode transfer menyimpan saldo utama secara lokal. Buat pemain, deposit dana, launch game, lalu gunakan endpoint ledger untuk audit atau koreksi terkontrol. Setiap spin mencatat bet sebagai row debit dan kemenangan sebagai row credit di ledger wallet.

  1. Create the player with POST /api/v1/users.
  2. Fund it with POST /api/v1/wallet/deposit.
  3. Launch game crabbysgold.
  4. Use balance and transactions for reconciliation.
  5. Rollback one completed debit or credit only when required.
  1. Buat pemain dengan POST /api/v1/users.
  2. Isi dana dengan POST /api/v1/wallet/deposit.
  3. Launch game crabbysgold.
  4. Gunakan balance dan transactions untuk rekonsiliasi.
  5. Rollback satu debit atau credit completed hanya bila diperlukan.

Balance

Returns the current authoritative local balance. This read does not create a ledger row.

Mengembalikan saldo lokal utama saat ini. Operasi baca ini tidak membuat row ledger.

GET/api/v1/wallet/balance
external_user_id string required
currency         string required
RequestRequest
GET /api/v1/wallet/balance?external_user_id=player001&currency=USD
Success responseRespons sukses
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "balance_amount": 100000000,
    "currency": "USD",
    "timestamp": "2026-06-21T12:00:00Z"
  }
}

Deposit

Credits a transfer player's local balance and creates one completed credit ledger row.

Menambah saldo lokal pemain transfer dan membuat satu row ledger credit berstatus completed.

POST/api/v1/wallet/deposit
operator_id      string  required
external_user_id string  required
reference_id     string  required, unique per operator
amount           integer required, 1..1000000000000
currency         string  required
Request bodyBody request
{
  "operator_id": "<operator_uuid>",
  "external_user_id": "player001",
  "reference_id": "deposit-20260621-0001",
  "amount": 100000000,
  "currency": "USD"
}
Response data fieldsField data respons
id, operator_id, user_id, external_user_id,
wallet_type, type, amount, currency,
balance_before, balance_after, reference_id,
status, failure_code, metadata,
created_at, completed_at

Withdraw

Debits the local balance for an operator-controlled transfer-out. Insufficient funds do not change the balance.

Mengurangi saldo lokal untuk transfer-out yang dikontrol operator. Saldo tidak cukup tidak mengubah balance.

POST/api/v1/wallet/withdraw

The body shape is identical to deposit. Use a new reference_id for every distinct transfer.

Bentuk body sama dengan deposit. Gunakan reference_id baru untuk setiap transfer yang berbeda.

Request bodyBody request
{
  "operator_id": "<operator_uuid>",
  "external_user_id": "player001",
  "reference_id": "withdraw-20260621-0001",
  "amount": 1000000,
  "currency": "USD"
}

Debit & Credit

These provider-neutral wallet operations are used for game financial movement. Debit removes funds; credit adds funds. The game engine uses the same wallet gateway internally with a round-scoped reference of the form round:<actionId>:bet.

Operasi wallet netral-provider ini digunakan untuk pergerakan finansial game. Debit mengurangi dana; credit menambah dana. Game engine memakai wallet gateway yang sama secara internal dengan reference per-round berbentuk round:<actionId>:bet.

POST/api/v1/wallet/debit
POST/api/v1/wallet/credit

Unlike deposit and withdraw, these bodies do not contain operator_id.

Berbeda dari deposit dan withdraw, body ini tidak memiliki operator_id.

Debit or credit bodyBody debit atau credit
{
  "external_user_id": "player001",
  "reference_id": "round:4338747140720652:bet",
  "amount": 100,
  "currency": "USD"
}
Response dataData respons
{
  "transaction_id": "659c881f-8afd-44f5-b35e-57f75dd07aa2",
  "balance_after": 99999900,
  "currency": "USD",
  "timestamp": "2026-06-21T12:00:00Z"
}

Rollback

Reverses one eligible completed debit or credit. The backend derives the original amount and currency; do not send them.

Membalik satu debit atau credit completed yang memenuhi syarat. Backend mengambil amount dan currency dari transaksi awal; jangan mengirimkannya.

POST/api/v1/wallet/rollback
Request bodyBody request
{
  "external_user_id": "player001",
  "original_reference_id": "round:4338747140720652:bet",
  "rollback_reference_id": "round:4338747140720652:rollback"
}
Important codesKode penting
TRANSACTION_NOT_FOUND
TRANSACTION_NOT_ROLLBACKABLE
TRANSACTION_ALREADY_ROLLED_BACK
IDEMPOTENCY_CONFLICT

Transactions

Lists operator-owned ledger rows. Filters are optional. Default limit is 20; maximum limit is 100; maximum offset is 10000.

Menampilkan row ledger milik operator. Filter bersifat opsional. Limit default 20; limit maksimum 100; offset maksimum 10000.

GET/api/v1/wallet/transactions
external_user_id optional
type             optional: credit, debit, rollback
status           optional: pending, completed, failed,
                           reversed, mismatch
reference_id     optional
limit            optional: 1..100
offset           optional: 0..10000
RequestRequest
GET /api/v1/wallet/transactions?external_user_id=player001&status=completed&limit=20&offset=0
Response shapeBentuk respons
{
  "status": true,
  "code": "SUCCESS",
  "data": {
    "items": [],
    "limit": 20,
    "offset": 0
  }
}

Transfer Wallet Test Checklist

Checklist Pengujian Transfer Wallet

  • Create one active player and verify duplicate creation returns USER_ALREADY_EXISTS.
  • Deposit, balance-check, withdraw, debit, credit, and rollback.
  • Launch crabbysgold, perform a spin that wins, and verify both the debit and credit rows exist in the ledger.
  • Open the launch URL, play a few spins, and confirm bets/wins appear in GET /api/v1/wallet/transactions.
  • Repeat identical idempotent requests concurrently and verify one financial effect.
  • Reuse a key with a different amount and expect IDEMPOTENCY_CONFLICT.
  • Verify insufficient debit leaves balance unchanged.
  • Buat satu pemain aktif dan pastikan duplikat menghasilkan USER_ALREADY_EXISTS.
  • Uji deposit, balance, withdraw, debit, credit, dan rollback.
  • Launch crabbysgold, lakukan satu spin yang menang, lalu pastikan row debit dan credit muncul di ledger.
  • Buka launch URL, mainkan beberapa spin, dan pastikan bet/win muncul di GET /api/v1/wallet/transactions.
  • Ulangi request idempotent identik secara concurrent dan pastikan hanya ada satu efek finansial.
  • Pakai ulang key dengan amount berbeda dan harapkan IDEMPOTENCY_CONFLICT.
  • Pastikan debit dengan saldo tidak cukup tidak mengubah balance.

Error Codes

Kode Error

Handle errors by stable code. Do not depend on a human-readable message because public API errors intentionally do not include one.

Tangani error berdasarkan code yang stabil. Jangan bergantung pada pesan untuk manusia karena error API publik memang tidak menyertakannya.