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 |
| 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.
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 |
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`.
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 |