# PokerLeagueOS > The operating system for poker leagues: seasons, schedules, standings, seating, blinds and payouts for the organizer, plus a live companion for every player. An agent acting on behalf of a human can create an organization, issue an API key, and start a live tournament in a single HTTP call. Human companion UI lives at https://pokerleagueos.com. ## Try it in one request ``` curl -X POST https://pokerleagueos.com/api/quickstart \ -H "content-type: application/json" \ -d '{"org_name":"Demo League"}' ``` The response contains: - `api_key` — `ck_live_…` Bearer token. Shown exactly once. Store it. - `org_slug` — use in every follow-up URL: `/api/orgs/{org_slug}/…`. - `session_id` — pre-provisioned demo tournament with DEFAULT_BLINDS. - `next_actions[]` — the first endpoints an agent will typically call. - `expires_at` — trial orgs auto-delete 7 days after creation unless a Stripe card is added. **Not idempotent.** A blind retry after network timeout creates a second trial org — see `hints.retry_safety` in the response. Rate limit: 5 trial orgs per hour per IP (advisory — best-effort on serverless). ### Immediately after quickstart ``` # Start the clock (use api_key as Bearer, session_id from the response) curl -X POST https://pokerleagueos.com/api/orgs/{org_slug}/clock \ -H "Authorization: Bearer {api_key}" \ -H "content-type: application/json" \ -d '{"sessionId":"{session_id}","action":"start"}' # Add a player curl -X POST https://pokerleagueos.com/api/orgs/{org_slug}/players \ -H "Authorization: Bearer {api_key}" \ -H "content-type: application/json" \ -d '{"nickname":"Alice"}' ``` ## API Reference - [OpenAPI 3.1 spec](https://pokerleagueos.com/openapi.json) — machine-readable. Feed this to Vercel AI SDK, OpenAI Agents SDK, or any OpenAPI-aware tool-discovery framework. - [llms-full.txt](https://pokerleagueos.com/llms-full.txt) — expanded reference with example requests, response shapes, and common agent workflows. ## Core Concepts - **Organization** — Tenant unit. Every row is scoped by `org_id`. URL-addressable by slug: `/api/orgs/{slug}/...`. Roles: `owner > admin > manager > player`. - **Session** — A single tournament event. Status: `active` | `paused` | `completed` | `cancelled`. Has `blinds_config`, `buy_in_amount`, `payout_structure`, `starting_stack`. - **Clock** — Real-time tournament timer. Advances through blind levels, auto-advances when time hits zero. Drift-corrected via `last_updated_at`. - **Roster Player** — Named player in an org. Carries a 4-digit `pin` for self-service. - **Check-in** — Links a roster player to a session with payment status and rebuy/addon counts. - **Elimination** — Records when a player busts, who eliminated them, and the resulting position. - **Payout structure** — Percentage-based prize distribution. Presets: `top3`, `top5`. - **Table assignment** — Maps players to tables and seats. Supports seat-on-pay. - **Trial org** — Org created via `POST /api/quickstart`. `created_via = "quickstart"`, `trial_ends_at = +7 days`. Auto-deleted by daily cleanup cron unless converted (v1.1). ## Authentication Three modes. Agents should use API keys. - **API key (agents)** — `Authorization: Bearer ck_live_`. Org-scoped. Issued by `POST /api/quickstart` (new orgs) or `POST /api/orgs/{slug}/api-keys` (existing orgs). Revoke via the same route's DELETE. - **Supabase session cookie (humans)** — Set by the web UI login flow. Not suitable for agents. - **Session UUID (public, read-only)** — Public endpoints accept a session UUID as a read-only access token. Private sessions return 404. - **Player PIN** — 4-digit PIN on `roster_players.pin` gates self-service player actions (rebuy requests, re-seat requests). ## Agent-Friendly Conventions - **Errors** — Every non-2xx response returns a flat envelope: `{ error, code, message, hint, doc_url, next_actions }`. `error` is the legacy string for frontend compat. Agents should read `code`, `hint`, and `next_actions[]` to choose the next tool call. - **Idempotency** — Some mutating endpoints accept an `Idempotency-Key: ` header; the same key within 24h returns the cached response, so a timeout is safe to retry. Concurrent duplicates are safe too: the key is reserved before the handler runs, and the second request gets 409 `idempotency_key_in_flight` rather than executing a second time. `/openapi.json` marks which ones (look for the `Idempotency-Key` parameter): the clock, eliminations POST and DELETE, session create, check-in, rebuys, api-keys, setups create, player-requests and the four Library collections. Everywhere else the header is ignored. - Exception: `POST /api/quickstart` is deliberately **not** idempotent — each call creates a fresh trial org. - **Rate limits** — 429 responses include `Retry-After` header (seconds). Per-IP limits: 5/hour quickstart, 30/min clock, 60/min public, 10/min PIN, 5/min auth. - **Consistent envelopes** — List endpoints return `{ data: [...], next_cursor, total }`. Single-resource endpoints return the resource directly. - **Multi-tenancy** — Every data row belongs to exactly one org. API keys cannot cross org boundaries. ## Public Endpoints (no auth, session UUID only) - [/api/quickstart](https://pokerleagueos.com/api/quickstart) — POST only. Creates a trial org, session, clock, and API key in one request. See top of this file. - [/api/health](https://pokerleagueos.com/api/health) — Liveness probe. Returns `{ ok: true, ts }`. - [/api/live-tournament?sessionId={uuid}](https://pokerleagueos.com/api/live-tournament) — Single-request snapshot of all live tournament state — clock, blinds, players, eliminations, prize pool, table stats. - [/api/clock?sessionId={uuid}](https://pokerleagueos.com/api/clock) — Clock state only (current level, time remaining, running/paused). - [/api/public-table-assignments?sessionId={uuid}](https://pokerleagueos.com/api/public-table-assignments) — Who's sitting at which table and seat. - [/api/public-player-requests](https://pokerleagueos.com/api/public-player-requests) — Submit a rebuy/re-seat/knockout request (POST, requires the player's PIN). Hosts read the pending queue through `GET /api/orgs/{slug}/sessions/{id}/player-requests`. - [/api/public-player-pin](https://pokerleagueos.com/api/public-player-pin) — Set or verify a player's 4-digit PIN. Set requires nickname match to prevent PIN-spraying. - [/api/public-feed?sessionId={uuid}](https://pokerleagueos.com/api/public-feed) — Chronological feed of tournament events. - [/api/public-session-timeline?sessionId={uuid}](https://pokerleagueos.com/api/public-session-timeline): host-action timeline for this tournament. - [/api/public-season-standings?sessionId={uuid}](https://pokerleagueos.com/api/public-season-standings): standings for the season this tournament belongs to. - [/api/public-active-season-standings?sessionId={uuid}](https://pokerleagueos.com/api/public-active-season-standings): live/provisional season standings while the tournament is still running. Eliminated players hold their real finish position; every player still alive is scored at the next position to be awarded, so values move on each bust. - [/api/public-minigame-leaderboard?sessionId={uuid}](https://pokerleagueos.com/api/public-minigame-leaderboard): minigame winners and per-player tallies. - [/api/public-player-career?sessionId={uuid}&rosterPlayerId={uuid}](https://pokerleagueos.com/api/public-player-career): one player's career history, achievements, and head-to-head record. `scope` selects all-time vs season. - [/api/public-player-tournament?sessionId={uuid}&tournamentSessionId={uuid}&rosterPlayerId={uuid}](https://pokerleagueos.com/api/public-player-tournament): one player's detail for one past tournament. Every public endpoint above is keyed by the **live** `sessionId`. That UUID is the access token and the only tenant key a public caller supplies. Private sessions return 404. ## Authenticated Endpoints (org-scoped) All under `/api/orgs/{slug}/...`. Require a Supabase session cookie or an API key. ### Sessions (tournaments) - `GET|POST /api/orgs/{slug}/sessions` — List or create tournaments. - `GET|PATCH|DELETE /api/orgs/{slug}/sessions/{id}` — Single tournament. PATCH takes `is_private` to hide or unhide every public surface for it. - `POST /api/orgs/{slug}/sessions/{id}/clone` — Clone for re-run. Carries the full config, including bounties, minigames, cutoff levels and privacy. - `GET|POST|DELETE /api/orgs/{slug}/sessions/{id}/participants` — Add or drop a player for one tournament (`{ rosterPlayerId, checkIn? }`). - `POST /api/orgs/{slug}/sessions/{id}/managers/handoff` — Mint a single-use co-host link (default 30 minutes). - `POST|DELETE /api/orgs/{slug}/sessions/{id}/check-in` — Check a player in, or undo it. - `POST /api/orgs/{slug}/sessions/{id}/rebuys` — Record or remove a rebuy/add-on (`{ rosterPlayerId, type, action }`). Counts come back on the check-in and live-tournament reads. - `GET|POST /api/orgs/{slug}/sessions/{id}/results` — Final standings. Rows take `rosterPlayerId` + `finishPosition`; `points` and `payoutAmount` are resolved from the session's point chart and payout structure when omitted. `pointsOverride` (org admin only) replaces a player's season points and requires `pointsOverrideReason`, which the player is shown; send `null` to clear one. - `GET /api/orgs/{slug}/sessions/{id}/participants` — Who's in this session. - `GET|POST|DELETE /api/orgs/{slug}/sessions/{id}/managers` — Session-level manager delegation. - `POST /api/orgs/{slug}/sessions/{id}/notify` — Send email notifications. - `GET /api/orgs/{slug}/sessions/{id}/player-requests` — Pending player self-service requests (host side). - `PATCH /api/orgs/{slug}/sessions/{id}/player-requests` — Resolve one: `{ requestId, action: accept|decline|revoke }`. - `POST /api/orgs/{slug}/sessions/{id}/player-requests` — Record a minigame result: `{ targetPlayerId, minigame, hand? }`. - `GET|POST /api/orgs/{slug}/sessions/{id}/rsvp` — RSVP management. - `GET|POST /api/orgs/{slug}/sessions/{id}/timeline`: host-action timeline for a tournament (read, or append an entry). - `PATCH /api/orgs/{slug}/sessions/{id}/minigame-order`: reorder minigame results (sets the displayed ranking). ### Clock - `GET /api/orgs/{slug}/clock?sessionId={uuid}` — Authenticated clock state. - `POST /api/orgs/{slug}/clock` — Actions: `start`, `pause`, `resume`, `next-level`, `prev-level`, `set-level`, `adjust-time`. ### Roster & players - `GET|POST /api/orgs/{slug}/players` — Roster list/create. - `GET|PATCH /api/orgs/{slug}/players/{playerId}` — Single player. - `GET|POST|DELETE /api/orgs/{slug}/eliminations` — List, record, or undo an elimination. - `GET|POST /api/orgs/{slug}/table-assignments` — Seat management. ### Org admin - `GET|PATCH|DELETE /api/orgs/{slug}/members` — Org membership. PATCH changes a member's role. - `GET|PATCH /api/orgs/{slug}/settings` — Org settings. - `GET|POST /api/orgs/{slug}/invites` — Invite tokens. - `GET|POST|DELETE /api/orgs/{slug}/api-keys` — API key management. Cannot be invoked via a Bearer key — a key cannot manage other keys. Use the web UI. - `GET|POST /api/orgs/{slug}/setups`: tournament templates ("Setup Templates" in the UI Library). A template holds everything a tournament is created with — blinds, chips, payouts, rebuy/add-on rules and cutoff levels, bounties, minigames, points, seating, persona, expected size. It does not hold `is_private`; that stays a per-event choice. - `GET|PUT|DELETE /api/orgs/{slug}/setups/{setupId}` — Single template. PUT is partial; omitted fields keep their value. `blind_schedule_id` / `payout_schedule_id` link the template to a Library block, which wins over the embedded `blinds_config` / `payout_structure` snapshot when the template is used. Send null to detach; omit them and the link drops only if you send a snapshot that differs from the stored one. A block owned by another org is stored as null. - `POST /api/orgs/{slug}/setups/{setupId}/duplicate`: copy a template. - `GET /api/orgs/{slug}/plan-status`: current plan, limits, and trial state. - `GET /api/orgs/{slug}/leaderboard` — Org leaderboard. - `GET|POST /api/orgs/{slug}/seasons` — Season CRUD. - `GET|PATCH|DELETE /api/orgs/{slug}/seasons/{seasonId}` — Single season. - `GET /api/orgs/{slug}/seasons/{seasonId}/standings` — Season standings. ### Library (reusable building blocks) Org-owned blocks a tournament or a setup template can reference. All four follow the same shape: `GET|POST` on the collection, `PATCH|DELETE` on `/{id}`. A tournament snapshots the block's content at creation, so editing a block never rewrites a game already in progress. - `GET|POST /api/orgs/{slug}/blind-schedules` · `PATCH|DELETE /api/orgs/{slug}/blind-schedules/{id}`: named blind structures. - `GET|POST /api/orgs/{slug}/chip-sets` · `PATCH|DELETE /api/orgs/{slug}/chip-sets/{id}`: chip colors, denominations, quantities. Drives color-up accuracy. - `GET|POST /api/orgs/{slug}/payout-schedules` · `PATCH|DELETE /api/orgs/{slug}/payout-schedules/{id}`: payout tables by player count. - `GET|POST /api/orgs/{slug}/point-charts` · `PATCH|DELETE /api/orgs/{slug}/point-charts/{id}`: league points by finishing position and player count. Applied against the paid-entrant count when results are saved; falls back to the built-in scale when a tournament has no chart. - `POST|DELETE /api/orgs/{slug}/league-rules`: upload or remove the org's league-rules PDF (multipart `file`, admin only, 4 MB max). Players read it from the companion. ### Billing - `POST /api/orgs/{slug}/billing/checkout` — Create Stripe Checkout session. - `POST /api/orgs/{slug}/billing/portal` — Create Stripe Customer Portal session. ## Common Agent Workflows ### "Start a tournament from scratch" 1. `POST /api/quickstart` with `{ "org_name": "..." }` — returns `api_key`, `org_slug`, `session_id`. 2. `POST /api/orgs/{slug}/players` with `{ "nickname": "Alice" }` — add players as needed. 3. `POST /api/orgs/{slug}/clock` with `{ sessionId, action: "start" }` — start the clock. 4. `GET /api/live-tournament?sessionId={uuid}` — confirm running state. ### "Who's still in?" 1. `GET /api/live-tournament?sessionId={uuid}` — `remaining` field + `recentEliminations[]`. ### "Set up payouts and league points once, reuse every week" 1. `POST /api/orgs/{slug}/payout-schedules` with `{ name, rows }`, one row per player-count tier. 2. `POST /api/orgs/{slug}/point-charts` with `{ name, rows }`, points by finishing position per tier. 3. `POST /api/orgs/{slug}/sessions` referencing them. The tournament snapshots the payout structure and resolves points from the chart when results are saved. ### "Knock out player X" 1. `GET /api/orgs/{slug}/players` — find player ID by nickname. 2. `POST /api/orgs/{slug}/eliminations` with `{ sessionId, rosterPlayerId, eliminatedById? }`. ## Comparisons - [/vs/the-tournament-director](https://pokerleagueos.com/vs/the-tournament-director) - [/vs/blindvalet](https://pokerleagueos.com/vs/blindvalet) ## Resources - [Main site](https://pokerleagueos.com) - [Agents overview](https://pokerleagueos.com/agents) - [Pricing](https://pokerleagueos.com/pricing) - [Terms](https://pokerleagueos.com/terms) - [Privacy](https://pokerleagueos.com/privacy) ## Versioning & Stability Current API version: **v1** (stable). Breaking changes announced ≥30 days in advance. Deprecated fields remain functional for 90 days after announcement. Check `/openapi.json` `info.version` for exact build.