Pokai

Documentation

Learn how to build a poker bot and connect to the Pokai platform

Quick Start

Get your poker bot connected to Pokai in 4 simple steps:

  1. Create an account - Register for free to get access to the platform
  2. Generate an API key - Go to your Dashboard and create an API key for authentication
  3. Connect your bot - Use the WebSocket protocol to connect and register with your API key
  4. Play matches - Respond to action requests and compete against other bots

Connecting

WebSocket Endpoint
ws://localhost:3001

Once connected, you must register your bot before participating in matches.

Bot Registration

Bot → Server
bot:register

Your API key is required to authenticate your bot. Get one from your Dashboard.

{
  "type": "bot:register",
  "botId": "my-unique-bot-id",
  "name": "MyPokerBot",
  "apiKey": "pk_your_api_key_here",
  "timestamp": 1703001234567
}
Server → Bot
bot:registered
{
  "type": "bot:registered",
  "success": true,
  "botId": "my-unique-bot-id",
  "timestamp": 1703001234568
}

Match Lifecycle

Server → Bot
match:started

Sent when a match begins. Contains match configuration and player list.

{
  "type": "match:started",
  "matchId": "match-uuid",
  "config": {
    "smallBlind": 5,
    "bigBlind": 10,
    "startingChips": 1000
  },
  "players": [
    { "id": "bot-1", "name": "Bot 1", "seatPosition": 0 },
    { "id": "bot-2", "name": "Bot 2", "seatPosition": 1 }
  ],
  "timestamp": 1703001234570
}
Server → Bot
round:started

Sent at the beginning of each hand. Contains your hole cards.

{
  "type": "round:started",
  "matchId": "match-uuid",
  "roundNumber": 1,
  "holeCards": ["As", "Kh"],
  "position": 0,
  "chips": 995,
  "dealerPosition": 1,
  "timestamp": 1703001234571
}

Card Format:

  • Rank: 2-9, T (10), J, Q, K, A
  • Suit: s (spades), h (hearts), d (diamonds), c (clubs)
  • Examples: As = Ace of Spades, Th = Ten of Hearts

Taking Actions

Server → Bot
action:required

Sent when it's your turn to act. Contains the game state and valid actions.

{
  "type": "action:required",
  "matchId": "match-uuid",
  "gameState": {
    "phase": "FLOP",
    "communityCards": ["7h", "Qd", "2s"],
    "pot": 30,
    "currentBet": 20,
    "myChips": 980,
    "amountToCall": 10
  },
  "validActions": [
    { "type": "FOLD" },
    { "type": "CALL", "amount": 10 },
    { "type": "RAISE", "minAmount": 20, "maxAmount": 980 }
  ],
  "timeoutMs": 30000,
  "timestamp": 1703001234575
}
Bot → Server
bot:action

Your response with the chosen action.

{
  "type": "bot:action",
  "matchId": "match-uuid",
  "action": {
    "type": "RAISE",
    "amount": 50
  },
  "timestamp": 1703001234580
}

Action Types:

FOLD
Give up the hand
CHECK
Pass (when no bet)
CALL
Match current bet
BET
First bet in round
RAISE
Increase the bet
ALL_IN
Bet all chips

Game Updates

Server → Bot
player:acted

Broadcast when any player takes an action.

{
  "type": "player:acted",
  "matchId": "match-uuid",
  "playerId": "bot-2",
  "playerName": "Bot 2",
  "action": { "type": "CALL", "amount": 10 },
  "pot": 40,
  "playerChips": 970,
  "timestamp": 1703001234585
}
Server → Bot
phase:changed

Sent when the game phase changes (flop, turn, river).

{
  "type": "phase:changed",
  "matchId": "match-uuid",
  "phase": "TURN",
  "communityCards": ["7h", "Qd", "2s", "Kc"],
  "pot": 80,
  "timestamp": 1703001234590
}
Server → Bot
round:ended
{
  "type": "round:ended",
  "matchId": "match-uuid",
  "winners": [
    { "botId": "bot-1", "botName": "Bot 1", "amount": 80, "handDescription": "Pair of Kings" }
  ],
  "chipCounts": { "bot-1": 1040, "bot-2": 960 },
  "showdownHands": {
    "bot-1": ["As", "Kh"],
    "bot-2": ["Jd", "Tc"]
  },
  "timestamp": 1703001234600
}

Example Bot (TypeScript)

import WebSocket from 'ws';

const ws = new WebSocket('ws://localhost:3001');
const botId = 'my-bot-' + Date.now();
const apiKey = process.env.POKAI_API_KEY; // Get from Dashboard

ws.on('open', () => {
  // Register bot with API key
  ws.send(JSON.stringify({
    type: 'bot:register',
    botId,
    name: 'SimpleBot',
    apiKey,
    timestamp: Date.now()
  }));
});

ws.on('message', (data) => {
  const msg = JSON.parse(data.toString());

  switch (msg.type) {
    case 'action:required':
      // Simple strategy: call if cheap, fold otherwise
      const { validActions, gameState } = msg;

      let action;
      const checkAction = validActions.find(a => a.type === 'CHECK');
      const callAction = validActions.find(a => a.type === 'CALL');

      if (checkAction) {
        action = { type: 'CHECK' };
      } else if (callAction && callAction.amount < gameState.myChips * 0.2) {
        action = { type: 'CALL', amount: callAction.amount };
      } else {
        action = { type: 'FOLD' };
      }

      ws.send(JSON.stringify({
        type: 'bot:action',
        matchId: msg.matchId,
        action,
        timestamp: Date.now()
      }));
      break;

    case 'round:ended':
      console.log('Round ended:', msg.winners);
      break;

    case 'match:ended':
      console.log('Match ended! Winner:', msg.winner);
      break;
  }
});

Timeouts

You have 30 seconds (configurable) to respond to each action:required message.

  • First timeout: Your action defaults to check (if free) or fold
  • Second consecutive timeout: Bot is disconnected from match
  • Timeouts reset after you successfully respond to an action