Webhook Endpoints
Withdraw
Deduct player balance for bets
POST {api_base_url}/withdraw
Called when a player places a bet. Deduct the amount from the player's balance.
Request
{
"player_id": "player_123",
"amount": 5000,
"game": "aviator",
"instance_id": "inst_abc",
"action": "BET",
"action_id": "bet_789",
"tx_id": "withdraw:bet:bet_789"
}| Field | Type | Description |
|---|---|---|
player_id | string | Player to deduct from |
amount | integer | Amount in cents (5000 = 50.00) |
game | string | Game identifier |
instance_id | string | Game instance identifier |
action | string | "BET" |
action_id | string | Unique bet identifier |
tx_id | string | Transaction ID, format: withdraw:bet:{action_id} |
Response
{"type": "SUCCESS", "balance": 950.00, "timestamp": 1712401234567}On error:
{"type": "ERROR", "code": "INSUFFICIENT_BALANCE", "balance": 50.00}Always return HTTP 200. Use the type field to indicate success or failure.
Error Codes
| Code | When to return |
|---|---|
INSUFFICIENT_BALANCE | Player doesn't have enough funds. Include current balance. |
PLAYER_NOT_FOUND | Unknown player ID |
INVALID_REQUEST | Missing or malformed fields |
Idempotency
If you receive a withdraw with a tx_id you've already processed, return the same SUCCESS response with the current balance. Do not deduct again.
This will happen in production - network issues cause retries. If your implementation doesn't track tx_id, players will be double-charged.
Concurrency
Multiple withdraws for the same player can arrive simultaneously (the player might have bets in multiple games). Use row-level locking or atomic operations to prevent race conditions.
BEGIN;
SELECT balance FROM players WHERE id = $1 FOR UPDATE;
-- check balance >= amount, check tx_id not already processed
UPDATE players SET balance = balance - $2 WHERE id = $1;
INSERT INTO transactions (tx_id, player_id, amount, type) VALUES ($3, $1, $2, 'WITHDRAW');
COMMIT;