Phoenix Games
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"
}
FieldTypeDescription
player_idstringPlayer to deduct from
amountintegerAmount in cents (5000 = 50.00)
gamestringGame identifier
instance_idstringGame instance identifier
actionstring"BET"
action_idstringUnique bet identifier
tx_idstringTransaction 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

CodeWhen to return
INSUFFICIENT_BALANCEPlayer doesn't have enough funds. Include current balance.
PLAYER_NOT_FOUNDUnknown player ID
INVALID_REQUESTMissing 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;