Files
jackboxpartypack-gamepicker/docs/plans/2026-03-15-api-documentation-implementation.md
cottongin 8ba32e128c docs: comprehensive API documentation from source code
Replace existing docs with fresh documentation built entirely from source
code analysis. OpenAPI 3.1 spec as source of truth, plus human-readable
Markdown with curl examples, response samples, and workflow guides.

- OpenAPI 3.1 spec covering all 42 endpoints (validated against source)
- 7 endpoint reference docs (auth, games, sessions, picker, stats, votes, webhooks)
- WebSocket protocol documentation (auth, subscriptions, 4 event types)
- 4 guide documents (getting started, session lifecycle, voting, webhooks)
- API README with overview, auth docs, and quick reference table
- Old docs archived to docs/archive/

Made-with: Cursor
2026-03-15 16:44:53 -04:00

30 KiB

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/*.mddocs/archive/
  • Create directories: docs/api/, docs/api/endpoints/, docs/api/guides/

Step 1: Create the archive directory

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

Step 2: Move existing docs to archive

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

ls -R docs/

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

Step 4: Commit

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:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Step 3: Final commit if any files were missed

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