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
4.8 KiB
4.8 KiB
Votes Endpoints
Real-time popularity voting. Bots or integrations send votes during live gaming sessions. Votes are matched to the currently-playing game using timestamp intervals.
Endpoint Summary
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/votes |
None | Paginated vote history with filtering |
| POST | /api/votes/live |
Bearer | Submit a live vote (up/down) |
GET /api/votes
Paginated vote history with filtering. Use query parameters to filter by session, game, username, or vote type.
Authentication
None.
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| session_id | int | No | — | Filter by session ID |
| game_id | int | No | — | Filter by game ID |
| username | string | No | — | Filter by voter username |
| vote_type | string | No | — | "up" or "down" |
| page | int | No | 1 | Page number |
| limit | int | No | 50 | Items per page (max 100) |
Response
200 OK
Results are ordered by timestamp DESC. The vote_type field is returned as "up" or "down" (not raw integers).
{
"votes": [
{
"id": 891,
"session_id": 5,
"game_id": 42,
"game_title": "Quiplash 3",
"pack_name": "Party Pack 7",
"username": "viewer123",
"vote_type": "up",
"timestamp": "2026-03-15T20:29:55.000Z",
"created_at": "2026-03-15T20:29:56.000Z"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 237,
"total_pages": 5
}
}
Error Responses
| Status | Body | When |
|---|---|---|
| 400 | { "error": "..." } |
Invalid session_id, game_id, or vote_type |
| 200 | { "votes": [], "pagination": { "page": 1, "limit": 50, "total": 0, "total_pages": 0 } } |
No results match the filters |
POST /api/votes/live
Submit a real-time up/down vote for the game currently being played. Automatically finds the active session and matches the vote to the correct game using the provided timestamp and session game intervals.
Authentication
Bearer token required. Include in header: Authorization: Bearer <token>.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| username | string | Yes | Identifier for the voter (used for deduplication) |
| vote | string | Yes | "up" or "down" |
| timestamp | string | Yes | ISO 8601 timestamp when the vote occurred |
{
"username": "viewer123",
"vote": "up",
"timestamp": "2026-03-15T20:30:00Z"
}
Behavior
- Finds the active session (single session with
is_active = 1). - Matches the vote timestamp to the game being played at that time (uses interval between consecutive
played_attimestamps). - Updates game
upvotes,downvotes, andpopularity_scoreatomically in a transaction. - Deduplication: Rejects votes from the same username within a 1-second window (409 Conflict).
- Broadcasts a
vote.receivedWebSocket event to all clients subscribed to the active session. See WebSocket Protocol for event payload.
Response
200 OK
{
"success": true,
"message": "Vote recorded successfully",
"session": { "id": 3, "games_played": 5 },
"game": {
"id": 42,
"title": "Quiplash 3",
"upvotes": 11,
"downvotes": 2,
"popularity_score": 9
},
"vote": {
"username": "viewer123",
"type": "up",
"timestamp": "2026-03-15T20:30:00Z"
}
}
Error Responses
| Status | Body | When |
|---|---|---|
| 400 | { "error": "Missing required fields: username, vote, timestamp" } |
Missing required fields |
| 400 | { "error": "vote must be either \"up\" or \"down\"" } |
Invalid vote value |
| 400 | { "error": "Invalid timestamp format. Use ISO 8601 format (e.g., 2025-11-01T20:30:00Z)" } |
Invalid timestamp |
| 404 | { "error": "No active session found" } |
No session with is_active = 1 |
| 404 | { "error": "No games have been played in the active session yet" } |
Active session has no games |
| 404 | { "error": "Vote timestamp does not match any game in the active session", "debug": { "voteTimestamp": "2026-03-15T20:30:00Z", "sessionGames": [{ "title": "Quiplash 3", "played_at": "..." }] } } |
Timestamp outside any game interval |
| 409 | { "error": "Duplicate vote detected (within 1 second of previous vote)", "message": "Please wait at least 1 second between votes", "timeSinceLastVote": 0.5 } |
Same username voted within 1 second |
| 500 | { "error": "..." } |
Server error |
Example
curl -X POST "http://localhost:5000/api/votes/live" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "viewer123",
"vote": "up",
"timestamp": "2026-03-15T20:30:00Z"
}'