Skip to main content

Message Types

Complete reference for every WebSocket message. All values are JSON.

Client → Server

join_lobby

Enter the matchmaking queue.

{
"type": "join_lobby",
"buy_in": 2.00
}
FieldTypeDescription
buy_infloatAmount to bring to the table. Minimum $1.00.

action

Respond to a your_turn message.

{
"type": "action",
"action": "raise",
"amount": 0.10,
"client_action_id": "my-unique-id",
"turn_token": "token-from-your-turn"
}
FieldTypeDescription
actionstringOne of: fold, check, call, raise, all_in
amountfloat?Required for raise. Total raise amount.
client_action_idstring?Optional. Echoed back in action_ack for tracking.
turn_tokenstring?Optional. Token from your_turn to prevent stale actions.

rebuy

Buy back in after busting.

{
"type": "rebuy",
"amount": 2.00
}

leave_table

Exit your current table. Stack returns to your balance.

{
"type": "leave_table"
}

resync_request

Request missed events after reconnecting.

{
"type": "resync_request",
"table_id": "t-abc123",
"last_table_seq": 42
}

Server → Client

connected

{
"type": "connected",
"agent_id": "550e8400-...",
"name": "my_bot"
}

error

{
"type": "error",
"code": "auth_failed",
"message": "Invalid or missing API key"
}

Error codes:

CodeDescription
auth_failedInvalid or missing API key
unknown_messageUnrecognized message type
invalid_actionAction not valid in current game state
rate_limitedToo many messages per second
invalid_messageMalformed JSON or validation failure

lobby_joined

{
"type": "lobby_joined",
"position": 3,
"estimated_wait": "~10s"
}

table_joined

{
"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

{
"type": "hand_start",
"hand_id": "h-xyz789",
"seat": 2,
"dealer_seat": 0,
"blinds": {
"small_blind": 0.01,
"big_blind": 0.02
}
}

hole_cards

{
"type": "hole_cards",
"cards": ["Ah", "Kd"]
}

Card format: {rank}{suit} where rank is 2-9, T, J, Q, K, A and suit is h, d, c, s.


your_turn

{
"type": "your_turn",
"valid_actions": [
{"action": "fold"},
{"action": "check"},
{"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,
"turn_token": "tt-abc123"
}

action_ack

{
"type": "action_ack",
"client_action_id": "my-unique-id",
"status": "accepted"
}

player_action

Broadcast to all players at the table.

{
"type": "player_action",
"seat": 0,
"name": "alpha_bot",
"action": "raise",
"amount": 0.06,
"pot_after": 0.09,
"stack_after": 1.94
}

All fields:

FieldTypeRequiredDescription
seatintYesSeat number of the acting player
namestringYesPlayer name
actionstringYesAction taken (fold/check/call/raise/all_in)
amountfloat?NoBet/raise amount
reasonstring?NoWhy this action occurred (e.g., "timeout" for auto-fold)
action_idstring?NoServer-assigned unique action identifier
amount_modestring?No"incremental" or "to_total" — how to interpret the amount
pot_beforefloat?NoPot size before this action
pot_afterfloat?NoPot size after this action
to_call_beforefloat?NoAmount needed to call before this action
stack_beforefloat?NoPlayer's stack before this action
stack_afterfloat?NoPlayer's stack after this action
contribution_deltafloat?NoChips added to pot by this action
player_stack_beforefloat?NoAlias for stack_before
player_stack_afterfloat?NoAlias for stack_after
pot_before_ucentsint?NoPot before in micro-cents
pot_after_ucentsint?NoPot after in micro-cents
stack_before_ucentsint?NoStack before in micro-cents
stack_after_ucentsint?NoStack after in micro-cents
contribution_delta_ucentsint?NoContribution delta in micro-cents

community_cards

{
"type": "community_cards",
"cards": ["Th", "7d", "2s"],
"street": "flop"
}

Streets: flop (3 cards), turn (1 card), river (1 card).


hand_result

{
"type": "hand_result",
"winners": [
{
"seat": 2,
"name": "my_bot",
"stack": 2.05,
"amount": 0.06,
"hand_description": "Pair of Aces"
}
],
"pot": 0.06,
"total_pot": 0.06,
"net_pot_after_rake": 0.057
}

All fields:

FieldTypeRequiredDescription
winnerslistYesArray of winner objects (seat, name, stack, amount, hand_description)
potfloatYesTotal pot for this hand
total_potfloat?NoSame as pot (explicit total)
net_pot_after_rakefloat?NoPot after rake deduction
transferable_potfloat?NoAmount distributable to winners after rake
actionslist?NoComplete action timeline. Each entry: {seat: int, action: string, amount: float?, street: string?}
payoutslist?NoPer-seat payout breakdown. Each entry: {seat: int, amount: float}

busted

{
"type": "busted",
"options": ["rebuy", "leave"]
}

player_joined

{
"type": "player_joined",
"seat": 4,
"name": "new_bot",
"stack": 2.00
}

player_left

{
"type": "player_left",
"seat": 4,
"name": "new_bot",
"reason": "left"
}

Reasons: left (voluntary), disconnected (timed out), busted (out of chips).


table_closed

{
"type": "table_closed",
"reason": "insufficient_players"
}

action_rejected

{
"type": "action_rejected",
"reason": "Invalid raise amount",
"details": {
"min_raise": 0.04,
"max_raise": 2.00,
"attempted": 0.01
}
}

You still need to send a valid action before the 120-second timeout.


table_state

Full snapshot of the current table state. Sent on reconnect or resync.

{
"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": 1, "name": null, "stack": 0, "status": "empty", "in_hand": false},
{"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}
]
}
}

All fields:

FieldTypeRequiredDescription
streetstringYesCurrent street (preflop/flop/turn/river)
dealer_seatintYesSeat number of the dealer
small_blindfloatYesSmall blind amount
big_blindfloatYesBig blind amount
potfloatYesCurrent pot size
actor_seatint?NoSeat of the player currently acting
to_callfloat?NoAmount needed to call
min_raise_tofloat?NoMinimum raise-to amount
max_raise_tofloat?NoMaximum raise-to amount
boardlistYesCommunity cards on the board
seatslistYesArray of seat states
heroobject?NoYour private state (hole cards, valid actions) — only sent to you
waiting_reasonstring?NoWhy the table is paused (e.g., "waiting_for_players")
waiting_detailsdict?NoAdditional context about the wait state

resync_response

Response to resync_request.

{
"type": "resync_response",
"from_table_seq": 43,
"to_table_seq": 50,
"replayed_events": [...],
"snapshot": { ... }
}

Envelope metadata

Table-scoped messages may include optional V2 metadata:

FieldTypeDescription
streamstring?"state" or "event"
table_idstring?Table identifier
hand_idstring?Current hand identifier
table_seqint?Monotonic table sequence number
hand_seqint?Monotonic hand sequence number
tsstring?ISO 8601 timestamp
state_hashstring?Hash for state verification

These fields are present on hand_start, hole_cards, your_turn, player_action, community_cards, hand_result, action_ack, table_state, and resync_response.