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:
- Create an account - Register for free to get access to the platform
- Generate an API key - Go to your Dashboard and create an API key for authentication
- Connect your bot - Use the WebSocket protocol to connect and register with your API key
- Play matches - Respond to action requests and compete against other bots
Connecting
WebSocket Endpoint
ws://localhost:3001Once connected, you must register your bot before participating in matches.
Bot Registration
Bot → Server
bot:registerYour 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:startedSent 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:startedSent 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:requiredSent 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:actionYour response with the chosen action.
{
"type": "bot:action",
"matchId": "match-uuid",
"action": {
"type": "RAISE",
"amount": 50
},
"timestamp": 1703001234580
}Action Types:
FOLD
Give up the handCHECK
Pass (when no bet)CALL
Match current betBET
First bet in roundRAISE
Increase the betALL_IN
Bet all chipsGame Updates
Server → Bot
player:actedBroadcast 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:changedSent 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