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
972 lines
21 KiB
Markdown
972 lines
21 KiB
Markdown
# 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 A–Z and 0–9 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}'
|
||
```
|