Files
jackboxpartypack-gamepicker/docs/api/endpoints/sessions.md
cottongin 3ed3af06ba docs: add vote tracking endpoints to API documentation
Update REST endpoint docs (votes.md, sessions.md), WebSocket protocol
(websocket.md), OpenAPI spec, and voting guide with the new
GET /api/votes, GET /api/sessions/:id/votes, and vote.received event.

Made-with: Cursor
2026-03-15 19:16:23 -04:00

21 KiB
Raw Blame History

Sessions Endpoints

Sessions represent a gaming night. Only one session can be active at a time. Games are added to the active session as they're played. Sessions track game status, room codes, player counts, and chat logs for voting.

IMPORTANT: In session game sub-routes like /api/sessions/{sessionId}/games/{sessionGameId}/status, the sessionGameId parameter refers to the session_games.id row ID, NOT games.id.

Endpoint Summary

Method Path Auth Description
GET /api/sessions No List all sessions with games_played count
GET /api/sessions/active No Get the active session (or null)
GET /api/sessions/{id} No Get a session by ID
POST /api/sessions Bearer Create a new session
POST /api/sessions/{id}/close Bearer Close a session
DELETE /api/sessions/{id} Bearer Delete a closed session
GET /api/sessions/{id}/games No List games in a session
GET /api/sessions/{id}/votes No Get per-game vote breakdown for a session
POST /api/sessions/{id}/games Bearer Add a game to a session
POST /api/sessions/{id}/chat-import Bearer Import chat log for vote processing
GET /api/sessions/{id}/export Bearer Export session (JSON or TXT)
PATCH /api/sessions/{sessionId}/games/{sessionGameId}/status Bearer Update session game status
DELETE /api/sessions/{sessionId}/games/{sessionGameId} Bearer Remove game from session
PATCH /api/sessions/{sessionId}/games/{sessionGameId}/room-code Bearer Update room code for session game
POST /api/sessions/{sessionId}/games/{sessionGameId}/start-player-check Bearer Start room monitor
POST /api/sessions/{sessionId}/games/{sessionGameId}/stop-player-check Bearer Stop room monitor
PATCH /api/sessions/{sessionId}/games/{sessionGameId}/player-count Bearer Update player count for session game

GET /api/sessions

List all sessions with a games_played count. Ordered by created_at DESC.

Authentication

None.

Response

200 OK

[
  {
    "id": 5,
    "notes": "Friday game night",
    "is_active": 1,
    "created_at": "2026-03-15T19:00:00.000Z",
    "closed_at": null,
    "games_played": 3
  },
  {
    "id": 4,
    "notes": "Last week's session",
    "is_active": 0,
    "created_at": "2026-03-08T18:30:00.000Z",
    "closed_at": "2026-03-08T23:15:00.000Z",
    "games_played": 5
  }
]

Example

curl "http://localhost:5000/api/sessions"

GET /api/sessions/active

Get the active session. Returns the session object directly if one is active, or a wrapper with session: null if none.

Authentication

None.

Response

200 OK (active session exists)

{
  "id": 5,
  "notes": "Friday game night",
  "is_active": 1,
  "created_at": "2026-03-15T19:00:00.000Z",
  "closed_at": null,
  "games_played": 3
}

200 OK (no active session)

{
  "session": null,
  "message": "No active session"
}

Example

curl "http://localhost:5000/api/sessions/active"

GET /api/sessions/{id}

Get a single session by ID.

Authentication

None.

Path Parameters

Name Type Description
id integer Session ID

Response

200 OK

{
  "id": 5,
  "notes": "Friday game night",
  "is_active": 1,
  "created_at": "2026-03-15T19:00:00.000Z",
  "closed_at": null,
  "games_played": 3
}

Error Responses

Status Body When
404 { "error": "Session not found" } Invalid session ID

Example

curl "http://localhost:5000/api/sessions/5"

POST /api/sessions

Create a new session. Only one active session is allowed at a time. Triggers WebSocket session.started broadcast to all authenticated clients.

Authentication

Bearer token required.

Request Body

Field Type Required Description
notes string No Optional notes (e.g., "Friday game night")
{
  "notes": "Friday game night"
}

Response

201 Created

{
  "id": 5,
  "notes": "Friday game night",
  "is_active": 1,
  "created_at": "2026-03-15T19:00:00.000Z",
  "closed_at": null
}

Error Responses

Status Body When
400 { "error": "An active session already exists. Please close it before creating a new one.", "activeSessionId": 5 } An active session already exists

Example

curl -X POST "http://localhost:5000/api/sessions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"notes": "Friday game night"}'

POST /api/sessions/{id}/close

Close a session. Auto-sets all games with status playing to played. Optional body updates session notes. Triggers WebSocket session.ended broadcast to session subscribers.

Authentication

Bearer token required.

Path Parameters

Name Type Description
id integer Session ID

Request Body

Field Type Required Description
notes string No Optional notes (updates session notes)
{
  "notes": "Great session!"
}

Response

200 OK

{
  "id": 5,
  "notes": "Great session!",
  "is_active": 0,
  "created_at": "2026-03-15T19:00:00.000Z",
  "closed_at": "2026-03-15T23:30:00.000Z",
  "games_played": 4
}

Error Responses

Status Body When
400 { "error": "Session is already closed" } Session was already closed
404 { "error": "Session not found" } Invalid session ID

Example

curl -X POST "http://localhost:5000/api/sessions/5/close" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"notes": "Great session!"}'

DELETE /api/sessions/{id}

Delete a session. Cannot delete active sessions — close first. Cascades: deletes chat_logs and session_games.

Authentication

Bearer token required.

Path Parameters

Name Type Description
id integer Session ID

Response

200 OK

{
  "message": "Session deleted successfully",
  "sessionId": 5
}

Error Responses

Status Body When
400 { "error": "Cannot delete an active session. Please close it first." } Session is active
404 { "error": "Session not found" } Invalid session ID

Example

curl -X DELETE "http://localhost:5000/api/sessions/5" \
  -H "Authorization: Bearer $TOKEN"

GET /api/sessions/{id}/games

List all games in a session. Returns SessionGame objects joined with game data. Ordered by played_at ASC.

Authentication

None.

Path Parameters

Name Type Description
id integer Session ID

Response

200 OK

[
  {
    "id": 12,
    "session_id": 5,
    "game_id": 42,
    "manually_added": 1,
    "status": "played",
    "room_code": "ABCD",
    "played_at": "2026-03-15T19:15:00.000Z",
    "player_count": 6,
    "pack_name": "Jackbox Party Pack 7",
    "title": "Quiplash 3",
    "game_type": "Writing",
    "min_players": 3,
    "max_players": 8,
    "popularity_score": 12,
    "upvotes": 15,
    "downvotes": 3
  },
  {
    "id": 13,
    "session_id": 5,
    "game_id": 38,
    "manually_added": 0,
    "status": "playing",
    "room_code": "XY9Z",
    "played_at": "2026-03-15T20:00:00.000Z",
    "player_count": null,
    "pack_name": "Jackbox Party Pack 6",
    "title": "Trivia Murder Party 2",
    "game_type": "Trivia",
    "min_players": 1,
    "max_players": 8,
    "popularity_score": 8,
    "upvotes": 10,
    "downvotes": 2
  }
]

Example

curl "http://localhost:5000/api/sessions/5/games"

GET /api/sessions/{id}/votes

Get per-game vote breakdown for a session. Aggregates votes from the live_votes table by game. Results ordered by net_score DESC.

Authentication

None.

Path Parameters

Name Type Description
id integer Session ID

Response

200 OK

{
  "session_id": 5,
  "votes": [
    {
      "game_id": 42,
      "title": "Quiplash 3",
      "pack_name": "Party Pack 7",
      "upvotes": 14,
      "downvotes": 3,
      "net_score": 11,
      "total_votes": 17
    }
  ]
}

Returns 200 with an empty votes array when the session has no votes.

Error Responses

Status Body When
404 { "error": "Session not found" } Invalid session ID

Example

curl "http://localhost:5000/api/sessions/5/votes"

POST /api/sessions/{id}/games

Add a game to a session. Side effects: increments game play_count, sets previous playing games to played (skipped games stay skipped), triggers game.added webhook and WebSocket event, and auto-starts room monitor if room_code is provided.

Authentication

Bearer token required.

Path Parameters

Name Type Description
id integer Session ID

Request Body

Field Type Required Description
game_id integer Yes Game ID (from games table)
manually_added boolean No Whether the game was manually added (default: false)
room_code string No 4-character room code; if provided, auto-starts room monitor
{
  "game_id": 42,
  "manually_added": true,
  "room_code": "ABCD"
}

Response

201 Created

{
  "id": 14,
  "session_id": 5,
  "game_id": 42,
  "manually_added": 1,
  "status": "playing",
  "room_code": "ABCD",
  "played_at": "2026-03-15T20:30:00.000Z",
  "pack_name": "Jackbox Party Pack 7",
  "title": "Quiplash 3",
  "game_type": "Writing",
  "min_players": 3,
  "max_players": 8
}

Error Responses

Status Body When
400 { "error": "game_id is required" } Missing game_id
400 { "error": "Cannot add games to a closed session" } Session is closed
404 { "error": "Session not found" } Invalid session ID
404 { "error": "Game not found" } Invalid game_id

Example

curl -X POST "http://localhost:5000/api/sessions/5/games" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"game_id": 42, "manually_added": true, "room_code": "ABCD"}'

POST /api/sessions/{id}/chat-import

Import chat log and process votes. Matches votes to games by timestamp intervals. "thisgame++" = upvote, "thisgame--" = downvote. Deduplicates by SHA-256 hash of username:message:timestamp.

Authentication

Bearer token required.

Path Parameters

Name Type Description
id integer Session ID

Request Body

Field Type Required Description
chatData array Yes Array of { username, message, timestamp } objects
{
  "chatData": [
    {
      "username": "viewer1",
      "message": "thisgame++",
      "timestamp": "2026-03-15T20:30:00Z"
    },
    {
      "username": "viewer2",
      "message": "thisgame--",
      "timestamp": "2026-03-15T20:31:00Z"
    }
  ]
}

Response

200 OK

{
  "message": "Chat log imported and processed successfully",
  "messagesImported": 150,
  "duplicatesSkipped": 3,
  "votesProcessed": 25,
  "votesByGame": {
    "42": {
      "title": "Quiplash 3",
      "upvotes": 15,
      "downvotes": 2
    }
  },
  "debug": {
    "sessionGamesTimeline": [
      {
        "title": "Quiplash 3",
        "played_at": "2026-03-15T20:00:00.000Z",
        "played_at_ms": 1742068800000
      }
    ],
    "voteMatches": []
  }
}

Error Responses

Status Body When
400 { "error": "chatData must be an array" } chatData missing or not an array
400 { "error": "No games played in this session to match votes against" } Session has no games
404 { "error": "Session not found" } Invalid session ID

Example

curl -X POST "http://localhost:5000/api/sessions/5/chat-import" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"chatData":[{"username":"viewer1","message":"thisgame++","timestamp":"2026-03-15T20:30:00Z"}]}'

PATCH /api/sessions/{sessionId}/games/{sessionGameId}/status

Update the status of a session game. Valid values: playing, played, skipped. If setting to playing, auto-sets other playing games to played.

Note: sessionGameId is the session_games.id row ID, NOT games.id.

Authentication

Bearer token required.

Path Parameters

Name Type Description
sessionId integer Session ID
sessionGameId integer Session game ID (session_games.id)

Request Body

Field Type Required Description
status string Yes "playing", "played", or "skipped"
{
  "status": "played"
}

Response

200 OK

{
  "message": "Status updated successfully",
  "status": "played"
}

Error Responses

Status Body When
400 { "error": "Invalid status. Must be playing, played, or skipped" } Invalid status value
404 { "error": "Session game not found" } Invalid sessionId or sessionGameId

Example

curl -X PATCH "http://localhost:5000/api/sessions/5/games/14/status" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "played"}'

DELETE /api/sessions/{sessionId}/games/{sessionGameId}

Remove a game from a session. Stops room monitor and player count check.

Note: sessionGameId is the session_games.id row ID, NOT games.id.

Authentication

Bearer token required.

Path Parameters

Name Type Description
sessionId integer Session ID
sessionGameId integer Session game ID (session_games.id)

Response

200 OK

{
  "message": "Game removed from session successfully"
}

Error Responses

Status Body When
404 { "error": "Session game not found" } Invalid sessionId or sessionGameId

Example

curl -X DELETE "http://localhost:5000/api/sessions/5/games/14" \
  -H "Authorization: Bearer $TOKEN"

PATCH /api/sessions/{sessionId}/games/{sessionGameId}/room-code

Update the room code for a session game. Room code must be exactly 4 characters, uppercase AZ and 09 only (regex: ^[A-Z0-9]{4}$).

Note: sessionGameId is the session_games.id row ID, NOT games.id.

Authentication

Bearer token required.

Path Parameters

Name Type Description
sessionId integer Session ID
sessionGameId integer Session game ID (session_games.id)

Request Body

Field Type Required Description
room_code string Yes 4 uppercase alphanumeric chars (A-Z, 0-9)
{
  "room_code": "XY9Z"
}

Response

200 OK

Returns the updated SessionGame object with joined game data.

{
  "id": 14,
  "session_id": 5,
  "game_id": 42,
  "manually_added": 1,
  "status": "playing",
  "room_code": "XY9Z",
  "played_at": "2026-03-15T20:30:00.000Z",
  "pack_name": "Jackbox Party Pack 7",
  "title": "Quiplash 3",
  "game_type": "Writing",
  "min_players": 3,
  "max_players": 8,
  "popularity_score": 12,
  "upvotes": 15,
  "downvotes": 3
}

Error Responses

Status Body When
400 { "error": "room_code is required" } Missing room_code
400 { "error": "room_code must be exactly 4 alphanumeric characters (A-Z, 0-9)" } Invalid format
404 { "error": "Session game not found" } Invalid sessionId or sessionGameId

Example

curl -X PATCH "http://localhost:5000/api/sessions/5/games/14/room-code" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"room_code": "XY9Z"}'

GET /api/sessions/{id}/export

Export session data as a file download. JSON format includes structured session, games, and chat_logs. TXT format is human-readable plaintext.

Authentication

Bearer token required.

Path Parameters

Name Type Description
id integer Session ID

Query Parameters

Name Type Required Default Description
format string No txt "json" or "txt"

Response

200 OK

  • JSON format: Content-Type application/json, filename session-{id}.json
{
  "session": {
    "id": 5,
    "created_at": "2026-03-15T19:00:00.000Z",
    "closed_at": "2026-03-15T23:30:00.000Z",
    "is_active": false,
    "notes": "Friday game night",
    "games_played": 4
  },
  "games": [
    {
      "title": "Quiplash 3",
      "pack": "Jackbox Party Pack 7",
      "players": "3-8",
      "type": "Writing",
      "played_at": "2026-03-15T19:15:00.000Z",
      "manually_added": true,
      "status": "played"
    }
  ],
  "chat_logs": [
    {
      "username": "viewer1",
      "message": "thisgame++",
      "timestamp": "2026-03-15T19:20:00.000Z",
      "vote": "thisgame++"
    }
  ]
}
  • TXT format: Content-Type text/plain, filename session-{id}.txt — human-readable sections with headers.

Error Responses

Status Body When
404 { "error": "Session not found" } Invalid session ID

Example

# JSON export
curl -o session-5.json "http://localhost:5000/api/sessions/5/export?format=json" \
  -H "Authorization: Bearer $TOKEN"

# TXT export (default)
curl -o session-5.txt "http://localhost:5000/api/sessions/5/export" \
  -H "Authorization: Bearer $TOKEN"

POST /api/sessions/{sessionId}/games/{sessionGameId}/start-player-check

Start the room monitor for a session game. The game must have a room code.

Note: sessionGameId is the session_games.id row ID, NOT games.id.

Authentication

Bearer token required.

Path Parameters

Name Type Description
sessionId integer Session ID
sessionGameId integer Session game ID (session_games.id)

Response

200 OK

{
  "message": "Room monitor started",
  "status": "monitoring"
}

Error Responses

Status Body When
400 { "error": "Game does not have a room code" } Session game has no room_code
404 { "error": "Session game not found" } Invalid sessionId or sessionGameId

Example

curl -X POST "http://localhost:5000/api/sessions/5/games/14/start-player-check" \
  -H "Authorization: Bearer $TOKEN"

POST /api/sessions/{sessionId}/games/{sessionGameId}/stop-player-check

Stop the room monitor and player count check for a session game.

Note: sessionGameId is the session_games.id row ID, NOT games.id.

Authentication

Bearer token required.

Path Parameters

Name Type Description
sessionId integer Session ID
sessionGameId integer Session game ID (session_games.id)

Response

200 OK

{
  "message": "Room monitor and player count check stopped",
  "status": "stopped"
}

Example

curl -X POST "http://localhost:5000/api/sessions/5/games/14/stop-player-check" \
  -H "Authorization: Bearer $TOKEN"

PATCH /api/sessions/{sessionId}/games/{sessionGameId}/player-count

Manually update the player count for a session game. Sets player_count_check_status to completed. Broadcasts WebSocket player-count.updated.

Note: sessionGameId is the session_games.id row ID, NOT games.id.

Authentication

Bearer token required.

Path Parameters

Name Type Description
sessionId integer Session ID
sessionGameId integer Session game ID (session_games.id)

Request Body

Field Type Required Description
player_count integer Yes Non-negative player count
{
  "player_count": 6
}

Response

200 OK

{
  "message": "Player count updated successfully",
  "player_count": 6
}

Error Responses

Status Body When
400 { "error": "player_count is required" } Missing player_count
400 { "error": "player_count must be a positive number" } Invalid (NaN or negative)
404 { "error": "Session game not found" } Invalid sessionId or sessionGameId

Example

curl -X PATCH "http://localhost:5000/api/sessions/5/games/14/player-count" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"player_count": 6}'