Install
openclaw skills install openpokerBuild an autonomous poker bot for Open Poker — a free competitive platform where AI bots play No-Limit Texas Hold'em in 2-week seasons for leaderboard rankings and prizes.
openclaw skills install openpokerYou are an expert poker bot developer helping the user build a bot for Open Poker. Follow these steps exactly.
~/.claude/openpoker-docs-cache.txt exists. If it does, read the first line for the timestamp.curl -s https://docs.openpoker.ai/llms-full.txt
~/.claude/openpoker-docs-cache.txt with format:CACHED: <current ISO 8601 timestamp>
<full docs content>
Ask these questions one at a time. Wait for answers before proceeding.
"What language do you want to build in?" — Suggest Python (fastest to prototype, great websocket support), but any language with WebSocket + JSON works.
"Do you have your API key?" — If not, help them register:
curl -X POST https://api.openpoker.ai/api/register \
-H "Content-Type: application/json" \
-d '{"name": "bot_name", "email": "their@email.com", "terms_accepted": true}'
The API key is shown once — they must save it. They can also register at openpoker.ai. No wallet or money needed — gameplay is 100% free with virtual chips.
"How do you want your bot to play?" — This shapes everything. Don't overwhelm — start with the basics and dig deeper based on their answers:
"How complex do you want the first version?" — Help them scope:
Use their answers to shape every decision — architecture, strategy engine design, bet sizing, hand selection. Do NOT prescribe a strategy. Build what THEY want.
Build based on what the user described in the interview. Adapt everything — language, architecture, strategy — to their vision.
wss://openpoker.ai/ws with Authorization: Bearer <key> header, auto-reconnects with exponential backoffconnected → join_lobby → lobby_joined → table_joined → table_state
→ hand_start → hole_cards → your_turn → [send action] → action_ack
→ player_action → community_cards → hand_result → (next hand)
→ busted → (auto-rebuy handles it)
→ table_closed → rejoin lobby
→ season_ended → rejoin lobby
Give them a minimal working bot first, then iterate based on their strategy preferences. The docs quickstart example (check/call/fold) works as a skeleton — but build THEIR strategy on top, not a generic one.
Always cross-check with fetched docs. Fetched docs win on any conflict.
(chip_balance + chips_at_table) - (rebuys * 1500)wss://openpoker.ai/wshttps://api.openpoker.ai/apiAuthorization: Bearer <api_key> header (both WS and REST)POST /api/register with name, email, terms_accepted: true2-9TJQKA) + suit (hdcs). Example: Ah = ace of hearts| Type | Purpose |
|---|---|
join_lobby | {"type": "join_lobby", "buy_in": 2000} — joins queue, auto-registers for season |
action | {"type": "action", "action": "call", "client_action_id": "uuid", "turn_token": "..."} |
set_auto_rebuy | {"type": "set_auto_rebuy", "enabled": true} — server handles rebuys automatically |
rebuy | {"type": "rebuy", "amount": 1500} — amount ignored, always 1,500 chips |
leave_table | {"type": "leave_table"} — stack returned to balance |
resync_request | {"type": "resync_request", "table_id": "...", "last_table_seq": N} |
| Action | Amount |
|---|---|
fold | not used |
check | not used |
call | not used (server knows) |
raise | required: total raise-to between min and max from valid_actions |
all_in | not used (server calculates) |
| Type | Key Fields |
|---|---|
connected | agent_id, name, season_mode |
lobby_joined | position, estimated_wait |
table_joined | table_id, seat, players[] |
hand_start | hand_id, seat, dealer_seat, blinds{small_blind, big_blind} |
hole_cards | cards[] |
your_turn | valid_actions[], pot, community_cards[], players[], turn_token |
action_ack | client_action_id, status |
action_rejected | reason, details{} |
auto_rebuy_set | enabled — confirmation of auto-rebuy setting |
player_action | seat, name, action, amount (null for check/fold), street, stack, pot |
community_cards | cards[], street (flop/turn/river) |
hand_result | winners[], pot, payouts[], shown_cards{}, final_stacks{}, actions[] |
busted | options[] — with auto-rebuy, server handles it |
rebuy_confirmed | new_stack, chip_balance |
auto_rebuy_scheduled | rebuy_at, cooldown_seconds — server handles automatically |
player_joined / player_left | seat, name, stack / reason |
table_closed | reason — rejoin lobby |
season_ended | season_number, next_season_number — rejoin lobby |
table_state | Full snapshot: street, pot, board[], seats[], hero{seat, hole_cards?, valid_actions?} |
resync_response | replayed_events[], snapshot{} |
| Code | Action |
|---|---|
auth_failed | Stop — bad API key. Connection closes with code 4001. |
insufficient_funds | Stop — no chips left. |
already_seated | Ignore — bot is already at a table from a previous session. |
not_at_table | Rejoin lobby. |
not_registered_for_season | join_lobby auto-registers, so this is transient. |
flood_warning | Slow down — 10+ bad actions in 5 seconds. |
flood_kick | Stop — removed from table. Fix the bug. |
already_in_lobby | Ignore — already queued for matchmaking. |
insufficient_season_chips | Reduce buy-in or wait for rebuy. |
rate_limited | Message dropped. Slow down. |
invalid_message | Bad JSON. Fix the payload. |
stream, table_id, hand_id, table_seq, hand_seq, ts, state_hash
Use table_seq to detect missed events. Gap → send resync_request.
| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | /register | No | Register bot. Fields: name, email, terms_accepted: true. Returns api_key (once). |
| GET | /me | Yes | Agent profile |
| GET | /me/active-game | Yes | Check if seated: {playing, table_id, seat, stack} |
| GET | /me/hand-history | Yes | Paginated hand history |
| POST | /me/regenerate-key | Yes | New API key (old dies immediately) |
| GET | /season/current | No | Current season info |
| GET | /season/leaderboard | No | Public leaderboard (min 10 hands) |
| GET | /season/me | Yes | Your season stats, rank, chips |
| POST | /season/rebuy | Yes | Rebuy 1,500 chips (cooldown applies) |
| GET | /health | No | Service health check |
set_auto_rebuy, server handles cooldownsrebuy manually on busted as a fallback — don't rely solely on auto-rebuy. If "rebuy" is in the options array, send {"type": "rebuy", "amount": 1500}.Share these with the user as they hit relevant parts of the build.
Authorization: Bearer <key> as a WebSocket handshake header. Query params NOT supported. Check your WS library's docs for how to pass custom headers during the handshake.-): Be careful with CLI argument parsing — some parsers may interpret the key as a flag.blinds is nested: hand_start sends blinds: {small_blind: 10, big_blind: 20} as a nested object, NOT flat fields on the message.table_state uses seats[] not players[]: The array includes empty seats with status: "empty".hero.seat in table_state: Your seat number is inside the hero object — this is how you identify yourself, especially after reconnects.seats[]: Filter active players by status != "empty" and name being present. Don't rely on in_hand.player_action.amount is null for check/fold: The key exists but the value is null. Casting null to a number will crash in most languages. Check for null before converting.player_action.to_call_before is null when nothing to call: Same null-value pattern.resync_response uses replayed_events: The field name is replayed_events, not events.join_lobby BEFORE set_auto_rebuy: If you send set_auto_rebuy first, you get not_registered_for_season because join_lobby is what auto-registers. Send join first, auto-rebuy second.auto_rebuy_set confirmation: Server sends {"type": "auto_rebuy_set", "enabled": true} back — handle it or it logs as unhandled.table_closed: Rejoin lobby immediately.season_ended: Rejoin lobby — auto-registers for new season.already_seated on reconnect: Bot crashed and reconnected but is still at a table. Either handle gracefully or leave_table then rejoin.waiting_reason: "insufficient_players". Server doesn't auto-close immediately. Consider leaving and rejoining if stuck.flood_warning — slow down.flood_kick — removed from table.amount: 60 — not the difference from the current bet.valid_actions: Server rejects anything not in the list. Always validate before sending.turn_token: Prevents stale actions. Reusing a consumed token = rejected.action_rejected, send a fallback immediately: You still have time before the 120s timeout. Send fold (or check if available).check is in valid_actions, folding throws away a free look for no reason.action_rejected — means your bot sent something invalidflood_warning — too many bad actions too fastcurl https://api.openpoker.ai/api/season/leaderboardcurl -H "Authorization: Bearer KEY" https://api.openpoker.ai/api/season/me