Files
jackboxpartypack-gamepicker/docs/api/endpoints/sessions.md

972 lines
21 KiB
Markdown
Raw Normal View 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**
```json
[
{
"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
```bash
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)
```json
{
"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)
```json
{
"session": null,
"message": "No active session"
}
```
### Example
```bash
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**
```json
{
"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
```bash
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") |
```json
{
"notes": "Friday game night"
}
```
### Response
**201 Created**
```json
{
"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
```bash
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) |
```json
{
"notes": "Great session!"
}
```
### Response
**200 OK**
```json
{
"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
```bash
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**
```json
{
"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
```bash
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**
```json
[
{
"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
```bash
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**
```json
{
"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
```bash
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 |
```json
{
"game_id": 42,
"manually_added": true,
"room_code": "ABCD"
}
```
### Response
**201 Created**
```json
{
"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
```bash
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 |
```json
{
"chatData": [
{
"username": "viewer1",
"message": "thisgame++",
"timestamp": "2026-03-15T20:30:00Z"
},
{
"username": "viewer2",
"message": "thisgame--",
"timestamp": "2026-03-15T20:31:00Z"
}
]
}
```
### Response
**200 OK**
```json
{
"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
```bash
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"` |
```json
{
"status": "played"
}
```
### Response
**200 OK**
```json
{
"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
```bash
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**
```json
{
"message": "Game removed from session successfully"
}
```
### Error Responses
| Status | Body | When |
|--------|------|------|
| 404 | `{ "error": "Session game not found" }` | Invalid sessionId or sessionGameId |
### Example
```bash
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) |
```json
{
"room_code": "XY9Z"
}
```
### Response
**200 OK**
Returns the updated SessionGame object with joined game data.
```json
{
"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
```bash
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`
```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
```bash
# 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**
```json
{
"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
```bash
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**
```json
{
"message": "Room monitor and player count check stopped",
"status": "stopped"
}
```
### Example
```bash
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 |
```json
{
"player_count": 6
}
```
### Response
**200 OK**
```json
{
"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
```bash
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}'
```