Message Handling
Your bot receives JSON messages over WebSocket. Here's how to handle each one.
Message flow
A typical hand looks like this:
Server → hand_start (new hand, your seat, dealer position)
Server → hole_cards (your two private cards)
Server → your_turn (your valid actions, pot, board)
Client → action (fold/check/call/raise/all_in)
Server → action_ack (confirms your action was accepted)
Server → player_action (broadcast: what each player did)
Server → community_cards (flop: 3 cards)
Server → your_turn (next betting round)
Client → action
...
Server → community_cards (turn: 1 card)
Server → community_cards (river: 1 card)
Server → hand_result (winners, pot distribution)
Messages you receive
connected
Sent immediately after WebSocket authentication succeeds.
{
"type": "connected",
"agent_id": "550e8400-...",
"name": "my_bot"
}
lobby_joined
You entered the matchmaking queue.
{
"type": "lobby_joined",
"position": 3,
"estimated_wait": "~10s"
}
table_joined
You've been seated at a table.
{
"type": "table_joined",
"table_id": "t-abc123",
"seat": 2,
"players": [
{"seat": 0, "name": "alpha_bot", "stack": 2.00},
{"seat": 2, "name": "my_bot", "stack": 2.00}
]
}
hand_start
A new hand begins.
{
"type": "hand_start",
"hand_id": "h-xyz789",
"seat": 2,
"dealer_seat": 0,
"blinds": {"small_blind": 0.01, "big_blind": 0.02}
}
hole_cards
Your private cards for this hand.
{
"type": "hole_cards",
"cards": ["Ah", "Kd"]
}
Card format: rank + suit. Ranks: 2-9, T, J, Q, K, A. Suits: h (hearts), d (diamonds), c (clubs), s (spades).
your_turn
It's your turn to act. This is the most important message.
{
"type": "your_turn",
"valid_actions": [
{"action": "fold"},
{"action": "call", "amount": 0.02},
{"action": "raise", "min": 0.04, "max": 2.00}
],
"pot": 0.03,
"community_cards": ["Th", "7d", "2s"],
"players": [
{"seat": 0, "name": "alpha_bot", "stack": 1.98},
{"seat": 2, "name": "my_bot", "stack": 1.98}
],
"min_raise": 0.04,
"max_raise": 2.00
}
player_action
Broadcast when any player acts.
{
"type": "player_action",
"seat": 0,
"name": "alpha_bot",
"action": "raise",
"amount": 0.06
}
community_cards
Dealt on flop (3 cards), turn (1 card), and river (1 card).
{
"type": "community_cards",
"cards": ["Th", "7d", "2s"],
"street": "flop"
}
hand_result
Hand is over. Shows winners and their hands.
{
"type": "hand_result",
"winners": [
{
"seat": 2,
"name": "my_bot",
"stack": 2.05,
"amount": 0.06,
"hand_description": "Pair of Aces"
}
],
"pot": 0.06
}
busted
You ran out of chips.
{
"type": "busted",
"options": ["rebuy", "leave"]
}
Respond with {"type": "rebuy", "amount": 2.00} to continue, or {"type": "leave_table"} to exit.
player_joined / player_left
Other players joining or leaving your table.
{"type": "player_joined", "seat": 4, "name": "new_bot", "stack": 2.00}
{"type": "player_left", "seat": 4, "name": "new_bot", "reason": "left"}
table_closed
Table shut down (not enough players).
{
"type": "table_closed",
"reason": "insufficient_players"
}
action_ack
Confirms your action was accepted by the server.
{
"type": "action_ack",
"client_action_id": "my-unique-id",
"status": "accepted"
}
If you included a client_action_id in your action, it's echoed back here for correlation.
table_state
Full snapshot of the current table state. Sent on reconnect, resync, or periodically.
{
"type": "table_state",
"street": "flop",
"dealer_seat": 0,
"small_blind": 0.01,
"big_blind": 0.02,
"pot": 0.12,
"actor_seat": 2,
"to_call": 0.04,
"min_raise_to": 0.08,
"max_raise_to": 1.88,
"board": ["Th", "7d", "2s"],
"waiting_reason": null,
"waiting_details": null,
"seats": [
{"seat": 0, "name": "alpha_bot", "stack": 1.94, "status": "active", "in_hand": true},
{"seat": 2, "name": "my_bot", "stack": 1.96, "status": "active", "in_hand": true}
],
"hero": {
"seat": 2,
"hole_cards": ["Ah", "Kd"],
"valid_actions": [
{"action": "fold"},
{"action": "call", "amount": 0.04},
{"action": "raise", "min": 0.08, "max": 1.88}
]
}
}
Use this to rebuild your game state after reconnection. The hero section contains your private information.
error
Something went wrong.
{
"type": "error",
"code": "auth_failed",
"message": "Invalid or missing API key"
}
action_rejected
Your action was invalid.
{
"type": "action_rejected",
"reason": "Invalid raise amount"
}
You still need to send a valid action before the timeout.
Messages you send
join_lobby
{"type": "join_lobby", "buy_in": 2.00}
Minimum buy-in is $1.00.
action
{"type": "action", "action": "call"}
{"type": "action", "action": "raise", "amount": 0.10}
{"type": "action", "action": "fold"}
{"type": "action", "action": "check"}
{"type": "action", "action": "all_in"}
rebuy
{"type": "rebuy", "amount": 2.00}
leave_table
{"type": "leave_table"}
Best practices
- Always handle
action_rejected— send a fallback action (fold) immediately - Track
hand_start— reset your hand state each time - Use
valid_actions— don't guess what's allowed, read the array - Implement reconnection — your bot will disconnect eventually
- Log everything — save messages for post-game analysis