# API Documentation Implementation Plan

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Create comprehensive, accurate API documentation from source code — OpenAPI 3.1 spec as source of truth, plus human-readable Markdown with examples and guide-style prose.

**Architecture:** OpenAPI YAML spec covers all 41 REST endpoints. Separate Markdown files per route group with curl examples and response samples. WebSocket protocol documented in dedicated Markdown. Guide files connect endpoints into workflows.

**Tech Stack:** OpenAPI 3.1 YAML, Markdown, curl for examples

---

### Task 1: Archive existing docs and create directory structure

**Files:**
- Move: `docs/*.md` → `docs/archive/`
- Create directories: `docs/api/`, `docs/api/endpoints/`, `docs/api/guides/`

**Step 1: Create the archive directory**

```bash
mkdir -p docs/archive docs/api/endpoints docs/api/guides
```

**Step 2: Move existing docs to archive**

```bash
mv docs/API_QUICK_REFERENCE.md docs/archive/
mv docs/BOT_INTEGRATION.md docs/archive/
mv docs/SESSION_END_QUICK_START.md docs/archive/
mv docs/SESSION_END_WEBSOCKET.md docs/archive/
mv docs/SESSION_START_WEBSOCKET.md docs/archive/
mv docs/WEBSOCKET_FLOW_DIAGRAM.md docs/archive/
mv docs/WEBSOCKET_SUBSCRIPTION_GUIDE.md docs/archive/
mv docs/WEBSOCKET_TESTING.md docs/archive/
mv docs/todos.md docs/archive/
```

**Step 3: Verify structure**

```bash
ls -R docs/
```

Expected: `archive/` with old files, `api/endpoints/` and `api/guides/` empty, `plans/` with design docs.

**Step 4: Commit**

```bash
git add docs/
git commit -m "docs: archive old documentation, create new docs structure"
```

---

### Task 2: Write OpenAPI spec — info, servers, security, and component schemas

**Files:**
- Create: `docs/api/openapi.yaml`

**Step 1: Write the OpenAPI header, servers, security schemes, and all reusable component schemas**

Write `docs/api/openapi.yaml` with:

```yaml
openapi: 3.1.0
info:
  title: Jackbox Game Picker API
  description: API for managing Jackbox Party Pack games, sessions, voting, and integrations.
  version: "1.0"
  license:
    name: MIT

servers:
  - url: http://localhost:5000
    description: Local development (backend direct)
  - url: http://localhost:3000/api
    description: Docker Compose (via Vite/Nginx proxy)

security: []

tags:
  - name: Auth
    description: Authentication endpoints
  - name: Games
    description: Game management and filtering
  - name: Sessions
    description: Session lifecycle and game tracking
  - name: Picker
    description: Weighted random game selection
  - name: Stats
    description: Aggregate statistics
  - name: Votes
    description: Real-time popularity voting
  - name: Webhooks
    description: Webhook management for external integrations

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        JWT token obtained from POST /api/auth/login.
        Pass as `Authorization: Bearer <token>`. Tokens expire after 24 hours.

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
      required:
        - error

    Game:
      type: object
      properties:
        id:
          type: integer
        pack_name:
          type: string
        title:
          type: string
        min_players:
          type: integer
        max_players:
          type: integer
        length_minutes:
          type: integer
          nullable: true
        has_audience:
          type: integer
          enum: [0, 1]
        family_friendly:
          type: integer
          enum: [0, 1]
        game_type:
          type: string
          nullable: true
        secondary_type:
          type: string
          nullable: true
        play_count:
          type: integer
        popularity_score:
          type: integer
        upvotes:
          type: integer
        downvotes:
          type: integer
        enabled:
          type: integer
          enum: [0, 1]
        favor_bias:
          type: integer
          enum: [-1, 0, 1]
        created_at:
          type: string
          format: date-time

    Session:
      type: object
      properties:
        id:
          type: integer
        created_at:
          type: string
          format: date-time
        closed_at:
          type: string
          format: date-time
          nullable: true
        is_active:
          type: integer
          enum: [0, 1]
        notes:
          type: string
          nullable: true
        games_played:
          type: integer

    SessionGame:
      type: object
      properties:
        id:
          type: integer
        session_id:
          type: integer
        game_id:
          type: integer
        played_at:
          type: string
          format: date-time
        manually_added:
          type: integer
          enum: [0, 1]
        status:
          type: string
          enum: [playing, played, skipped]
        room_code:
          type: string
          nullable: true
        player_count:
          type: integer
          nullable: true
        player_count_check_status:
          type: string
          nullable: true
        pack_name:
          type: string
        title:
          type: string
        game_type:
          type: string
          nullable: true
        min_players:
          type: integer
        max_players:
          type: integer
        popularity_score:
          type: integer
        upvotes:
          type: integer
        downvotes:
          type: integer

    Pack:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        favor_bias:
          type: integer
          enum: [-1, 0, 1]
        created_at:
          type: string
          format: date-time

    PackMeta:
      type: object
      properties:
        name:
          type: string
        total_count:
          type: integer
        enabled_count:
          type: integer
        total_plays:
          type: integer

    Webhook:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        enabled:
          type: boolean
        created_at:
          type: string
          format: date-time

    WebhookLog:
      type: object
      properties:
        id:
          type: integer
        webhook_id:
          type: integer
        event_type:
          type: string
        payload:
          type: object
        response_status:
          type: integer
          nullable: true
        error_message:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
```

**Step 2: Validate YAML syntax**

```bash
node -e "const fs=require('fs'); const y=require('yaml'); y.parse(fs.readFileSync('docs/api/openapi.yaml','utf8')); console.log('Valid YAML')"
```

If `yaml` module not available, use: `npx -y yaml-cli docs/api/openapi.yaml` or manually verify structure.

**Step 3: Commit**

```bash
git add docs/api/openapi.yaml
git commit -m "docs: add OpenAPI spec with schemas and security definitions"
```

---

### Task 3: Write OpenAPI paths — Auth and Games endpoints

**Files:**
- Modify: `docs/api/openapi.yaml`

**Step 1: Add `paths` section with Auth endpoints**

Source: `backend/routes/auth.js` — 2 endpoints.

Add paths for:
- `POST /api/auth/login` — Body: `{ key }`, responses: 200 (token+message+expiresIn), 400 (missing key), 401 (invalid key)
- `POST /api/auth/verify` — Security: bearerAuth, responses: 200 (`{ valid, user: { role, timestamp } }`)

**Step 2: Add Games endpoints**

Source: `backend/routes/games.js` — 13 endpoints.

Add paths for:
- `GET /api/games` — Query params: `enabled`, `minPlayers`, `maxPlayers`, `playerCount`, `drawing` (only/exclude), `length` (short/medium/long), `familyFriendly`, `pack`. Response: array of Game.
- `GET /api/games/packs` — Response: array of Pack.
- `GET /api/games/meta/packs` — Response: array of PackMeta.
- `GET /api/games/export/csv` — Security: bearerAuth. Response: CSV file (text/csv).
- `PATCH /api/games/packs/{name}/favor` — Security: bearerAuth. Body: `{ favor_bias }` (-1/0/1). Response: `{ message, favor_bias }`. Error 400 for invalid value.
- `GET /api/games/{id}` — Response: Game or 404.
- `POST /api/games` — Security: bearerAuth. Body: `{ pack_name, title, min_players, max_players, length_minutes?, has_audience?, family_friendly?, game_type?, secondary_type? }`. Response 201: Game. Error 400: missing fields.
- `PUT /api/games/{id}` — Security: bearerAuth. Body: same fields (all optional). Response: Game or 404.
- `DELETE /api/games/{id}` — Security: bearerAuth. Response: `{ message }` or 404.
- `PATCH /api/games/{id}/toggle` — Security: bearerAuth. Response: Game (with toggled enabled) or 404.
- `PATCH /api/games/packs/{name}/toggle` — Security: bearerAuth. Body: `{ enabled }`. Response: `{ message, gamesAffected }`. Error 400: missing enabled.
- `POST /api/games/import/csv` — Security: bearerAuth. Body: `{ csvData, mode }` (mode: "append" or "replace"). Response: `{ message, count, mode }`. Error 400: missing csvData. CSV columns: Game Pack, Game Title, Min. Players, Max. Players, Length, Audience, Family Friendly?, Game Type, Secondary Type.
- `PATCH /api/games/{id}/favor` — Security: bearerAuth. Body: `{ favor_bias }` (-1/0/1). Response: `{ message, favor_bias }`. Error 400/404.

**Step 3: Validate YAML syntax**

Same validation command as Task 2 Step 2.

**Step 4: Commit**

```bash
git add docs/api/openapi.yaml
git commit -m "docs: add Auth and Games endpoint paths to OpenAPI spec"
```

---

### Task 4: Write OpenAPI paths — Sessions, Picker, Stats, Votes, Webhooks

**Files:**
- Modify: `docs/api/openapi.yaml`

**Step 1: Add Sessions endpoints (15 endpoints)**

Source: `backend/routes/sessions.js`

Add paths for:
- `GET /api/sessions` — Response: array of Session (with games_played count).
- `GET /api/sessions/active` — Response: Session object or `{ session: null, message }`.
- `GET /api/sessions/{id}` — Response: Session or 404.
- `POST /api/sessions` — Security: bearerAuth. Body: `{ notes? }`. Response 201: Session. Error 400: active session already exists (`{ error, activeSessionId }`). Triggers WebSocket `session.started` broadcast.
- `POST /api/sessions/{id}/close` — Security: bearerAuth. Body: `{ notes? }`. Response: closed Session (with games_played). Error 404/400 (already closed). Sets all 'playing' games to 'played'. Triggers WebSocket `session.ended`.
- `DELETE /api/sessions/{id}` — Security: bearerAuth. Response: `{ message, sessionId }`. Error 404/400 (cannot delete active). Cascades: deletes chat_logs and session_games.
- `GET /api/sessions/{id}/games` — Response: array of SessionGame (joined with game data).
- `POST /api/sessions/{id}/games` — Security: bearerAuth. Body: `{ game_id, manually_added?, room_code? }`. Response 201: SessionGame. Error 400 (closed session, missing game_id), 404 (session/game not found). Side effects: increments play_count, sets previous 'playing' games to 'played', triggers `game.added` webhook + WebSocket, auto-starts room monitor if room_code provided.
- `POST /api/sessions/{id}/chat-import` — Security: bearerAuth. Body: `{ chatData: [{ username, message, timestamp }] }`. Response: `{ message, messagesImported, duplicatesSkipped, votesProcessed, votesByGame, debug }`. Vote patterns: "thisgame++" = upvote, "thisgame--" = downvote. Matches votes to games by timestamp intervals.
- `PATCH /api/sessions/{sessionId}/games/{gameId}/status` — Security: bearerAuth. Body: `{ status }` (playing/played/skipped). Response: `{ message, status }`. Error 400/404. If setting to 'playing', auto-sets other playing games to 'played'.
- `DELETE /api/sessions/{sessionId}/games/{gameId}` — Security: bearerAuth. Response: `{ message }`. Error 404. Stops room monitor/player count check.
- `PATCH /api/sessions/{sessionId}/games/{gameId}/room-code` — Security: bearerAuth. Body: `{ room_code }` (exactly 4 chars, A-Z0-9). Response: SessionGame. Error 400 (invalid format)/404.
- `GET /api/sessions/{id}/export` — Security: bearerAuth. Query: `format` ("json" or "txt", default "txt"). Response: file download (application/json or text/plain).
- `POST /api/sessions/{sessionId}/games/{gameId}/start-player-check` — Security: bearerAuth. Response: `{ message, status: "monitoring" }`. Error 400 (no room code)/404.
- `POST /api/sessions/{sessionId}/games/{gameId}/stop-player-check` — Security: bearerAuth. Response: `{ message, status: "stopped" }`.
- `PATCH /api/sessions/{sessionId}/games/{gameId}/player-count` — Security: bearerAuth. Body: `{ player_count }` (non-negative integer). Response: `{ message, player_count }`. Error 400/404. Triggers WebSocket `player-count.updated`.

**Step 2: Add Picker endpoint**

Source: `backend/routes/picker.js` — mounted at `/api` (not `/api/picker`).

- `POST /api/pick` — No auth. Body: `{ playerCount?, drawing?, length?, familyFriendly?, sessionId?, excludePlayed? }`. Response 200: `{ game: Game, poolSize, totalEnabled }`. Response 404: `{ error, suggestion, recentlyPlayed? }`. Bias: game favor_bias 1=3x, -1=0.2x; pack favor_bias 1=2x, -1=0.3x. Repeat avoidance: excludes last 2 played games by default, or all played if excludePlayed=true.

**Step 3: Add Stats endpoint**

Source: `backend/routes/stats.js`

- `GET /api/stats` — No auth. Response: `{ games: { count }, gamesEnabled: { count }, packs: { count }, sessions: { count }, activeSessions: { count }, totalGamesPlayed: { count }, mostPlayedGames: [...], topRatedGames: [...] }`. The game arrays include: id, title, pack_name, play_count, popularity_score, upvotes, downvotes. Limited to top 10.

**Step 4: Add Votes endpoint**

Source: `backend/routes/votes.js`

- `POST /api/votes/live` — Security: bearerAuth. Body: `{ username, vote, timestamp }` where vote is "up" or "down", timestamp is ISO 8601. Response 200: `{ success, message, session: { id, games_played }, game: { id, title, upvotes, downvotes, popularity_score }, vote: { username, type, timestamp } }`. Error 400 (missing fields, invalid vote/timestamp), 404 (no active session, no games, vote doesn't match a game), 409 (duplicate within 1 second).

**Step 5: Add Webhooks endpoints (7 endpoints)**

Source: `backend/routes/webhooks.js`

- `GET /api/webhooks` — Security: bearerAuth. Response: array of Webhook (events parsed from JSON, enabled as boolean).
- `GET /api/webhooks/{id}` — Security: bearerAuth. Response: Webhook or 404.
- `POST /api/webhooks` — Security: bearerAuth. Body: `{ name, url, secret, events }` where events is string array. Response 201: Webhook + `{ message }`. Error 400: missing fields, invalid URL, events not array.
- `PATCH /api/webhooks/{id}` — Security: bearerAuth. Body: `{ name?, url?, secret?, events?, enabled? }`. Response: Webhook + `{ message }`. Error 400 (no fields, invalid URL, events not array)/404.
- `DELETE /api/webhooks/{id}` — Security: bearerAuth. Response: `{ message, webhookId }`. Error 404.
- `POST /api/webhooks/test/{id}` — Security: bearerAuth. Sends test `game.added` payload. Response: `{ message, note }`. Error 404.
- `GET /api/webhooks/{id}/logs` — Security: bearerAuth. Query: `limit` (default 50). Response: array of WebhookLog (payload parsed from JSON).

**Step 6: Add Health endpoint**

- `GET /health` — No auth. Response: `{ status: "ok", message: "Jackbox Game Picker API is running" }`.

**Step 7: Validate YAML syntax**

Same validation as before.

**Step 8: Commit**

```bash
git add docs/api/openapi.yaml
git commit -m "docs: complete all OpenAPI endpoint paths"
```

---

### Task 5: Write API README

**Files:**
- Create: `docs/api/README.md`

**Step 1: Write the API overview document**

Content should include:
- **Overview:** What the API does (manage Jackbox games, run sessions, track popularity, pick games with weighted randomness)
- **Base URL:** `http://localhost:5000` (direct) or `http://localhost:3000/api` (Docker proxy). All REST endpoints prefixed with `/api/` except `/health`.
- **Authentication:** POST to `/api/auth/login` with admin key → receive JWT. Include as `Authorization: Bearer <token>`. Tokens expire in 24 hours. Public endpoints (GET games, GET sessions, GET stats, POST pick, GET health) don't require auth. All write operations require auth.
- **Request/Response format:** JSON request/response. `Content-Type: application/json`. Exceptions: CSV export returns `text/csv`, session export can return `text/plain`.
- **Error handling:** All errors return `{ "error": "message" }`. HTTP status codes: 400 (bad request/validation), 401 (no token), 403 (invalid/expired token), 404 (not found), 409 (conflict/duplicate), 500 (server error).
- **Boolean fields:** SQLite stores booleans as integers (0/1). In request bodies, pass JS booleans; the API converts. In responses, expect 0/1 except for Webhook.enabled which returns a JS boolean.
- **Pagination:** No pagination — all list endpoints return full result sets.
- **Quick reference table:** All 41 endpoints in a single table: Method | Path | Auth | Description
- **Links:** to endpoint docs, WebSocket docs, and guides

**Step 2: Commit**

```bash
git add docs/api/README.md
git commit -m "docs: add API README with overview, auth, and quick reference"
```

---

### Task 6: Write endpoint docs — Auth and Games

**Files:**
- Create: `docs/api/endpoints/auth.md`
- Create: `docs/api/endpoints/games.md`

**Step 1: Write auth.md**

Cover the 2 auth endpoints with the template from the design doc. Include:
- Overview: simple admin-key authentication. One role (admin). No user management.
- Endpoint table
- For each endpoint: description, auth, parameters, request body, response, errors, curl example
- curl examples must use realistic sample data

**Step 2: Write games.md**

Cover all 13 games endpoints with the template. Important details to include from source code:
- GET /api/games filter behavior: `drawing=only` matches `game_type='Drawing'`, `drawing=exclude` excludes Drawing. `length=short` is ≤15min (including NULL), `medium` is 16-25min, `long` is >25min. Results ordered by pack_name, title.
- POST /api/games requires: pack_name, title, min_players, max_players. Optional: length_minutes, has_audience, family_friendly, game_type, secondary_type.
- PUT /api/games/:id uses COALESCE for most fields (only updates what's provided), but length_minutes, game_type, secondary_type accept explicit null.
- CSV import expects columns: Game Pack, Game Title, Min. Players, Max. Players, Length, Audience, Family Friendly?, Game Type, Secondary Type. Mode "replace" deletes ALL existing games first.
- Favor bias: 1=favor, -1=disfavor, 0=neutral. Applies to picker weighting.

**Step 3: Commit**

```bash
git add docs/api/endpoints/auth.md docs/api/endpoints/games.md
git commit -m "docs: add Auth and Games endpoint documentation"
```

---

### Task 7: Write endpoint docs — Sessions

**Files:**
- Create: `docs/api/endpoints/sessions.md`

**Step 1: Write sessions.md**

Cover all 15 sessions endpoints. Important details from source code:
- Only one active session at a time. Creating a session when one exists returns 400 with activeSessionId.
- Closing a session auto-sets all 'playing' games to 'played'.
- Cannot delete active sessions — must close first. Delete cascades chat_logs and session_games.
- Adding a game to session: auto-sets previous 'playing' games to 'played' (skipped games stay skipped), increments game play_count, triggers `game.added` webhook + WebSocket, auto-starts room monitor if room_code provided.
- Chat import: matches vote timestamps to games using interval logic (vote belongs to game whose played_at is most recent before vote timestamp). Deduplicates by SHA-256 hash of `username:message:timestamp`.
- Status update to 'playing' auto-sets other 'playing' games to 'played'.
- Room code: exactly 4 chars, uppercase A-Z and 0-9 only. Regex: `/^[A-Z0-9]{4}$/`.
- Export formats: JSON (structured with session+games+chat_logs) and TXT (human-readable plaintext).
- Player count: body is `{ player_count }`, must be non-negative integer. Sets `player_count_check_status` to 'completed'. Broadcasts `player-count.updated` via WebSocket.
- The `gameId` parameter in session game sub-routes refers to `session_games.id`, NOT `games.id`.

**Step 2: Commit**

```bash
git add docs/api/endpoints/sessions.md
git commit -m "docs: add Sessions endpoint documentation"
```

---

### Task 8: Write endpoint docs — Picker, Stats, Votes, Webhooks

**Files:**
- Create: `docs/api/endpoints/picker.md`
- Create: `docs/api/endpoints/stats.md`
- Create: `docs/api/endpoints/votes.md`
- Create: `docs/api/endpoints/webhooks.md`

**Step 1: Write picker.md**

Key details from source code:
- Filters enabled games only (enabled=1)
- Weighted random: game favor_bias 1 = 3x weight, -1 = 0.2x weight. Pack favor_bias 1 = 2x weight, -1 = 0.3x weight. Biases multiply.
- Repeat avoidance: with sessionId, excludes last 2 games by default. With excludePlayed=true, excludes ALL games played in session.
- 404 when no games match filters (with suggestion), or when all eligible games have been played.

**Step 2: Write stats.md**

Key details: single endpoint, no auth, returns aggregate counts and top-10 lists (most played, top rated). mostPlayedGames sorted by play_count DESC, topRatedGames sorted by popularity_score DESC, both only include games with > 0 in respective metric.

**Step 3: Write votes.md**

Key details:
- Requires auth. Body: `{ username, vote, timestamp }`.
- `vote` must be "up" or "down". `timestamp` must be valid ISO 8601.
- Automatically matches vote to the correct game in the active session using timestamp interval logic.
- Deduplication: rejects votes from same username within 1 second (409).
- Updates game upvotes/downvotes/popularity_score atomically in a transaction.

**Step 4: Write webhooks.md**

Key details:
- All endpoints require auth.
- Events stored as JSON string in DB, returned as parsed array.
- `enabled` stored as 0/1 in DB, returned as JS boolean.
- Secret is never returned in GET responses (excluded from SELECT).
- Test sends a `game.added` event with dummy data.
- Logs include parsed payload, limited by `limit` query param (default 50).

**Step 5: Commit**

```bash
git add docs/api/endpoints/picker.md docs/api/endpoints/stats.md docs/api/endpoints/votes.md docs/api/endpoints/webhooks.md
git commit -m "docs: add Picker, Stats, Votes, and Webhooks endpoint documentation"
```

---

### Task 9: Write WebSocket documentation

**Files:**
- Create: `docs/api/websocket.md`

**Step 1: Write websocket.md**

Cover the full WebSocket protocol from `backend/utils/websocket-manager.js`:

- **Connection:** `ws://host:port/api/sessions/live`. No query params needed. Connection established without auth.
- **Authentication:** Send `{ "type": "auth", "token": "<jwt>" }`. Server responds with `{ "type": "auth_success", "message": "Authenticated successfully" }` or `{ "type": "auth_error", "message": "..." }`. Auth required for subscribe/unsubscribe.
- **Subscription model:** Subscribe to a specific session's events with `{ "type": "subscribe", "sessionId": <number> }`. Response: `{ "type": "subscribed", "sessionId": <number>, "message": "..." }`. Can subscribe to multiple sessions. Unsubscribe with `{ "type": "unsubscribe", "sessionId": <number> }`.
- **Heartbeat:** Client sends `{ "type": "ping" }`, server responds `{ "type": "pong" }`. Timeout: 60 seconds since last ping — server terminates connection. Heartbeat check runs every 30 seconds.
- **Events (server → client):**
  - `session.started` — broadcast to ALL authenticated clients (not session-specific). Data: `{ session: { id, is_active, created_at, notes } }`. Triggered when `POST /api/sessions` creates a new session.
  - `game.added` — broadcast to session subscribers. Data: `{ session: { id, is_active, games_played }, game: { id, title, pack_name, min_players, max_players, manually_added, room_code } }`. Triggered when `POST /api/sessions/:id/games` adds a game.
  - `session.ended` — broadcast to session subscribers. Data: `{ session: { id, is_active, games_played } }`. Triggered when `POST /api/sessions/:id/close` closes session.
  - `player-count.updated` — broadcast to session subscribers. Data: `{ sessionId, gameId, playerCount, status }`. Triggered when player count is updated.
- **Event envelope:** `{ "type": "<event-type>", "timestamp": "<ISO 8601>", "data": { ... } }`
- **Error messages:** `{ "type": "error", "message": "..." }` for general errors, `{ "type": "auth_error", "message": "..." }` for auth failures.
- **Connection lifecycle example:** Connect → auth → subscribe → receive events → ping/pong loop → unsubscribe → close.
- **Reconnection:** Server doesn't maintain state across disconnects. Client must re-authenticate and re-subscribe after reconnecting.

**Step 2: Commit**

```bash
git add docs/api/websocket.md
git commit -m "docs: add WebSocket protocol documentation"
```

---

### Task 10: Write guide — Getting Started

**Files:**
- Create: `docs/api/guides/getting-started.md`

**Step 1: Write getting-started.md**

Narrative guide walking through the minimum viable integration:
1. Health check — verify API is running
2. Authenticate — get a JWT token
3. Browse games — list all games, filter by player count
4. Pick a game — use the picker with filters
5. Start a session — create session, add the picked game
6. Close the session

Each step includes a curl example and expected response. Cross-references the endpoint docs for full details.

**Step 2: Commit**

```bash
git add docs/api/guides/getting-started.md
git commit -m "docs: add Getting Started guide"
```

---

### Task 11: Write guide — Session Lifecycle

**Files:**
- Create: `docs/api/guides/session-lifecycle.md`

**Step 1: Write session-lifecycle.md**

Narrative guide covering:
1. Creating a session (one active at a time constraint)
2. Adding games (via picker or manual), understanding auto-status-transitions
3. Tracking game status (playing → played/skipped)
4. Room codes and player count monitoring
5. Closing sessions (auto-finalizes playing games)
6. Exporting session data (JSON and TXT formats)
7. Deleting old sessions (must close first, cascades)
8. WebSocket integration for real-time updates

**Step 2: Commit**

```bash
git add docs/api/guides/session-lifecycle.md
git commit -m "docs: add Session Lifecycle guide"
```

---

### Task 12: Write guide — Voting and Popularity

**Files:**
- Create: `docs/api/guides/voting-and-popularity.md`

**Step 1: Write voting-and-popularity.md**

Narrative guide covering:
1. How popularity works: `popularity_score = upvotes - downvotes`
2. Two voting mechanisms: chat import (batch, after-the-fact) and live votes (real-time, from bots)
3. Chat import flow: collect chat logs with `thisgame++`/`thisgame--` patterns, POST to chat-import, timestamp-matching algorithm explained
4. Live vote flow: bot sends votes in real-time via POST /api/votes/live, same timestamp-matching logic, deduplication within 1 second
5. How voting affects the picker: popularity_score doesn't directly affect picker weights (favor_bias does), but topRatedGames in stats uses it
6. Favor bias vs popularity: favor_bias is admin-controlled weighting for the picker; popularity is community-driven sentiment tracking

**Step 2: Commit**

```bash
git add docs/api/guides/voting-and-popularity.md
git commit -m "docs: add Voting and Popularity guide"
```

---

### Task 13: Write guide — Webhooks and Events

**Files:**
- Create: `docs/api/guides/webhooks-and-events.md`

**Step 1: Write webhooks-and-events.md**

Narrative guide covering:
1. Two notification systems: Webhooks (HTTP callbacks) and WebSocket (persistent connections)
2. When to use which: Webhooks for server-to-server integrations (bots, Discord); WebSocket for real-time UI or tools that maintain persistent connections
3. Webhook setup: create webhook, specify events, provide URL and secret
4. Webhook events: `game.added` (currently the only webhook event triggered in code — verify this). Payload shape.
5. Webhook delivery: async, logged in webhook_logs. Test with POST /api/webhooks/test/:id.
6. WebSocket events: `session.started`, `game.added`, `session.ended`, `player-count.updated`. Which are broadcast to all clients vs session subscribers.
7. Event payload reference (linking to websocket.md)

**Step 2: Commit**

```bash
git add docs/api/guides/webhooks-and-events.md
git commit -m "docs: add Webhooks and Events guide"
```

---

### Task 14: Validate documentation against source code

**Files:**
- Read: all `backend/routes/*.js` files
- Read: `docs/api/openapi.yaml`
- Read: all `docs/api/endpoints/*.md` files

**Step 1: Cross-reference every route handler against the OpenAPI spec**

For each file in `backend/routes/`:
- Count endpoints in source code
- Count corresponding paths in `openapi.yaml`
- Verify HTTP methods match
- Verify path patterns match (including parameter names)
- Verify auth requirements match (`authenticateToken` usage)

**Step 2: Verify request/response shapes**

For each endpoint:
- Compare request body fields to what the route handler destructures from `req.body`
- Compare response shapes to what the route handler actually sends via `res.json()`
- Check error status codes and messages match

**Step 3: Fix any discrepancies found**

Edit the OpenAPI spec and/or Markdown files to match the source code.

**Step 4: Commit any fixes**

```bash
git add docs/
git commit -m "docs: fix discrepancies found during validation"
```

---

### Task 15: Final review and summary commit

**Step 1: Verify all files exist**

```bash
ls -la docs/api/
ls -la docs/api/endpoints/
ls -la docs/api/guides/
ls -la docs/archive/
```

Expected:
- `docs/api/`: openapi.yaml, README.md, websocket.md
- `docs/api/endpoints/`: auth.md, games.md, sessions.md, picker.md, stats.md, votes.md, webhooks.md
- `docs/api/guides/`: getting-started.md, session-lifecycle.md, voting-and-popularity.md, webhooks-and-events.md
- `docs/archive/`: 9 old doc files

**Step 2: Verify total endpoint count in OpenAPI spec**

```bash
grep -c "operationId:" docs/api/openapi.yaml
```

Expected: 42 (41 API endpoints + 1 health check)

**Step 3: Final commit if any files were missed**

```bash
git add docs/
git status
git commit -m "docs: complete API documentation with OpenAPI spec, endpoint docs, WebSocket, and guides"
```
