Operator Wallet API guide
The Operator Wallet API is called by the Star Game platform, on your backend. You choose the base URL (for example https://api.example.com/game-wallet/v1) and enter it as Wallet URL in Console → Integration, per environment. The full machine-readable contract is the OpenAPI reference.
Conventions
Section titled “Conventions”- HTTPS only, JSON (UTF-8),
snake_casefields. Ignore unknown fields: adding fields is not a breaking change. - Every request is signed (see Signatures).
amountandbalanceare integer minor units as decimal strings ("1500"), pattern^-?[0-9]{1,19}$. The currency code and decimal places are the Currency and Decimals you configure in Console → Integration.- Identifiers are strings; times are RFC 3339 UTC.
- Whole-request errors use HTTP status codes with
application/problem+json(RFC 9457).
| Method | Path | Purpose |
|---|---|---|
POST |
/players/authenticate |
Exchange a launch code for a player and session |
POST |
/players/balance |
Current balance of a player |
POST |
/transactions |
Apply a batch of 1–500 debits, credits and rollbacks |
GET |
/transactions/{transaction_id} |
Look up one transaction (audit and support) |
POST /players/authenticate
Section titled “POST /players/authenticate”Called once per game launch with the code your App passed through getConfig.
POST /game-wallet/v1/players/authenticate HTTP/1.1Host: api.example.comContent-Type: application/jsonwebhook-id: msg_3f9a0c1e5b7d4e2a9c8b6a4f2e1d0c9bwebhook-timestamp: 1791763200webhook-signature: v1,K5o0…=
{"launch_code":"lc_7Qm2…","game_id":"lantern","client_ip":"203.0.113.24"}{ "player_id": "84021734", "session_id": "gs_1c9e…", "display_name": "Layla", "avatar_url": "https://cdn.example.com/a/84021734.png", "currency": "GOLD", "balance": "250000", "language": "ar", "country": "SA"}player_id, session_id, currency and balance are required. An unknown, used or expired code → 401 problem with code: "invalid_launch_code".
POST /players/balance
Section titled “POST /players/balance”{ "player_id": "84021734", "session_id": "gs_1c9e…" }{ "balance": "248500", "currency": "GOLD" }Unknown player → 404 player_not_found. Extend the session when this call carries a valid session_id.
POST /transactions
Section titled “POST /transactions”A batch of 1–500 transactions (at most your configured maximum batch size).
{ "transactions": [ { "transaction_id": "lantern:59666:84021734:bet", "type": "debit", "player_id": "84021734", "session_id": "gs_1c9e…", "amount": "1500", "currency": "GOLD", "game_id": "lantern", "round_id": "lantern:59666", "room_id": "8880085", "created_at": "2026-10-12T13:00:18Z", "details": { "bets": [{ "option": "rose", "amount": "1000" }, { "option": "phoenix", "amount": "500" }] } }, { "transaction_id": "lantern:59665:84021734:win", "type": "credit", "player_id": "84021734", "amount": "4500", "currency": "GOLD", "game_id": "lantern", "round_id": "lantern:59665", "reference_transaction_id": "lantern:59665:84021734:bet" }, { "transaction_id": "lantern:59664:84021734:bet:rollback", "type": "rollback", "player_id": "84021734", "game_id": "lantern", "round_id": "lantern:59664", "reference_transaction_id": "lantern:59664:84021734:bet" } ]}Response 200 with one result per item, in request order:
{ "results": [ { "transaction_id": "lantern:59666:84021734:bet", "status": "ok", "balance": "248500", "processed_at": "2026-10-12T13:00:18Z" }, { "transaction_id": "lantern:59665:84021734:win", "status": "ok", "balance": "253000", "processed_at": "2026-10-12T13:00:18Z" }, { "transaction_id": "lantern:59664:84021734:bet:rollback", "status": "ok", "balance": "253000", "processed_at": "2026-10-12T13:00:18Z" } ]}| Field | Types | Notes |
|---|---|---|
transaction_id |
all | Globally unique, ≤ 128 chars. Your idempotency key. |
type |
all | debit, credit or rollback |
player_id, game_id |
all | Required |
session_id |
debit | The session from authenticate |
amount, currency |
debit, credit | Minor units as a string |
round_id |
all | Groups all transactions of one round |
room_id |
optional | The room the player played from, if any |
reference_transaction_id |
rollback (required), credit (optional) | The debit to reverse / the debit being paid out |
created_at |
optional | When the platform created the transaction |
details |
optional | Human-readable breakdown for your support tools; treat as opaque |
Transaction status
Section titled “Transaction status”status |
Meaning | Booked? | What the platform does |
|---|---|---|---|
ok |
Booked now or earlier (also a rollback with nothing to reverse) | yes / already | Done |
insufficient_funds |
Debit refused, include balance |
no | Stake cancelled |
session_invalid |
Debit only: session ended or invalid | no | Stake cancelled |
player_blocked |
Debit only: player may not play | no | Stake cancelled |
limit_exceeded |
Debit only: your responsible-gaming or risk limit | no | Stake cancelled |
duplicate_mismatch |
Same transaction_id, different content |
no | Alert, manual follow-up |
reference_not_found |
Credit whose debit was never booked or was rolled back | no | Payout not made, recorded |
rolled_back |
Debit arriving after its rollback (tombstone) | no | Stake cancelled |
retry |
Transient failure (lock timeout etc.) | no | Resent with the same id |
GET /transactions/{transaction_id}
Section titled “GET /transactions/{transaction_id}”For audit and support only; the platform does not use it in the money path.
{ "transaction_id": "lantern:59666:84021734:bet", "type": "debit", "status": "ok", "amount": "1500", "balance": "248500", "rolled_back": false, "processed_at": "2026-10-12T13:00:18Z"}Never received → 404 transaction_not_found.
Errors (problem+json)
Section titled “Errors (problem+json)”Whole-request failures use a non-2xx status and an RFC 9457 body. The platform reads the HTTP status and code; type can be any URI you like.
HTTP/1.1 429 Too Many RequestsContent-Type: application/problem+jsonRetry-After: 2
{"type":"https://api.example.com/problems/rate_limited","title":"Too many requests","status":429,"code":"rate_limited","detail":"slow down"}code |
HTTP | When |
|---|---|---|
invalid_signature |
401 | Missing, wrong or stale signature |
invalid_launch_code |
401 | Unknown, used or expired launch code |
malformed_request |
400 | Body does not match the schema |
player_not_found |
404 | balance for an unknown player |
transaction_not_found |
404 | GET /transactions/{id} never received |
not_found |
404 | Unknown path |
rate_limited |
429 | Send Retry-After |
unavailable |
503 | Outcome unknown for the whole batch; send Retry-After |
internal |
500 | Unexpected error |
The platform retries 408, 429, 5xx and timeouts with the same transaction ids. See Error codes & rate limits.
Capacity declaration
Section titled “Capacity declaration”In Console → Integration (Sandbox or Production tab), under Wallet, you declare:
| Setting | Range | Effect |
|---|---|---|
| Max batch size | 1–500 | The platform never puts more transactions in one POST /transactions |
| Max concurrent requests | 1–16 | The platform never has more requests in flight to your wallet |
| Currency | A–Z, 0–9, 2–8 chars |
Sent as currency on debits and credits |
| Decimals | 0–8 | How your minor units map to display amounts |
Size these for your peak: at the close of a busy betting window many players are debited within a second or two. If you answer 429 or 503, the platform backs off according to Retry-After.
Signatures
Section titled “Signatures”Every request carries three headers (Standard Webhooks):
webhook-id: msg_<random> unique per HTTP attemptwebhook-timestamp: <unix seconds>webhook-signature: v1,<base64(HMAC-SHA256(key, id + "." + timestamp + "." + raw_body))>key is the base64-decoded part of your whsec_… secret. Reject timestamps more than 300 s from your clock. During secret rotation the header carries several space-separated signatures; accept the request if any matches. Full details and tested code in five languages: Signature verification.