Skip to main content
Gameplay messages carry gameType and tableId in the envelope and game-specific data in the payload. The core protocol routes these messages without inspecting the payload.

Turn-Based Messages

Used by sequential games (poker, blackjack) where one player acts at a time.
TypeDirectionDescription
game_action_requestS → C (unicast)It’s your turn; includes state + available actions
submit_actionC → SClient’s chosen action
game_state_updateS → AllGame state changed (new phase, cards, etc.)
player_action_broadcastS → AllAnother player’s action + resulting state
round_resultS → AllRound ended; winners, amounts, rake, fairness proof
game_errorS → CInvalid action or game-level error

game_action_request

Direction: Server → Client (unicast) Sent when it’s this player’s turn. The client MUST respond with submit_action within timeoutSeconds.
{
  type: "game_action_request",
  gameType: string,
  tableId: string,
  messageId: string,
  protocolVersion: "1.0",
  timeoutSeconds: number,        // Client MUST respond within this window
  timestamp: number,
  payload: {
    // Game-specific state visible to this player
    // + explicit availableActions array
  }
}
Every game_action_request payload MUST include an availableActions field. This is an explicit enumeration of the valid actions the client may take in the current state, including the action type and any required/optional parameters.
// Example from Texas Hold'em
payload: {
  holeCards: [...],
  gameState: {...},
  availableActions: [
    { type: "fold" },
    { type: "check" },
    { type: "call", callAmount: 50 },
    { type: "raise", minAmount: 100, maxAmount: 1000 },
    { type: "all_in" }
  ]
}
timeoutSeconds is enforced by the server’s clock. The client’s local clock is informational only. Budget at least 20% of the timeout for network round-trip.
If the client does not respond within timeoutSeconds, the server applies the default timeout action defined in the game specification (e.g., fold in poker, stand in blackjack).

submit_action

Direction: Client → Server
{
  type: "submit_action",
  gameType: string,
  tableId: string,
  messageId: string,
  protocolVersion: "1.0",
  timestamp: number,
  payload: {
    action: string,               // Action type (e.g., "fold", "hit", "place_bet")
    // ... action-specific parameters defined in game spec
  }
}
Clients MUST NOT send submit_action for a turn-based game except in response to game_action_request. Servers MUST reject unsolicited actions with NOT_YOUR_TURN.

game_state_update

Direction: Server → All clients at the table Broadcast when game state changes (cards dealt, community cards revealed, new phase, etc.).
{
  type: "game_state_update",
  gameType: string,
  tableId: string,
  payload: object,                // Game-specific state
  messageId: string,
  timestamp: number
}

player_action_broadcast

Direction: Server → All clients at the table Broadcast after a player’s action is processed.
{
  type: "player_action_broadcast",
  gameType: string,
  tableId: string,
  payload: {
    playerId: string,             // Address of acting player
    action: string,
    amount?: number,
    resultingState: object        // Updated game state
  },
  messageId: string,
  timestamp: number
}

round_result

Direction: Server → All clients at the table
{
  type: "round_result",
  gameType: string,
  tableId: string,
  messageId: string,
  timestamp: number,
  payload: {
    winners: [{
      playerId: string,
      grossAmount: number,
      rake: number,
      netAmount: number
    }],
    totalRake: number,
    fairnessProof?: {             // Present if server supports provably fair
      serverSeed: string,
      algorithm: string
    }
    // + game-specific result data
  }
}

game_error

Direction: Server → Client
{
  type: "game_error",
  gameType: string,
  tableId: string,
  code: string,
  message: string,
  relatedMessageId?: string,
  messageId: string,
  timestamp: number
}

Phase-Based Messages

Used by simultaneous-action games (roulette, baccarat) where all players act within a time window.
TypeDirectionDescription
betting_window_openS → AllBetting phase started; includes available bets + time limit
betting_window_closedS → AllNo more bets accepted
submit_actionC → SSame as turn-based (client places bet within window)
Phase-based games reuse game_state_update, round_result, and game_error from the turn-based set.

betting_window_open

Direction: Server → All clients
{
  type: "betting_window_open",
  gameType: string,
  tableId: string,
  messageId: string,
  protocolVersion: "1.0",
  timeoutSeconds: number,        // Window duration
  timestamp: number,
  payload: {
    // Game-specific: available bets, limits, previous results
    // MUST include availableActions field with valid bet types
  }
}
Clients MAY submit multiple submit_action messages during an open window (e.g., placing multiple roulette bets). Servers MUST reject actions received after betting_window_closed.

betting_window_closed

Direction: Server → All clients
{
  type: "betting_window_closed",
  gameType: string,
  tableId: string,
  messageId: string,
  timestamp: number
}
After the window closes, the server resolves the round and broadcasts round_result.

Client State Machine

For turn-based games:
IDLE → (receive game_action_request) → DECIDING → (send submit_action) → IDLE
For phase-based games:
IDLE → (receive betting_window_open) → BETTING → (send submit_action[s]) → BETTING
BETTING → (receive betting_window_closed) → IDLE

Multi-Table Play

When playing multiple concurrent games (requires capabilities.multiTable: true):
  1. Each incoming request includes gameType and tableId
  2. The client correlates the request to the correct game context
  3. The client’s response MUST include matching gameType and tableId
  4. Independent state machines are maintained per table
  5. Timeouts on one table are independent of actions on another