# API Reference This document describes the currently implemented API surfaces in Linear Void. ## Base URLs - Public app and admin: - WebSocket endpoint: - Local fallback: and ## Authentication Model Admin REST endpoints use a cookie session. - Cookie name: linearvoid_admin_session - Cookie attributes: HttpOnly, SameSite=Strict, Secure when request is HTTPS (or X-Forwarded-Proto=https) - Session TTL: controlled by ADMIN_SESSION_TTL_HOURS (default 12h) ## Chat WebSocket API ### Connect Endpoint: - GET /ws?username={name}&room={room} Client sends JSON messages over the socket. ### Message Envelope (client -> server) ```json { "room": "main", "username": "viewer123", "text": "hello world", "type": "public" } ``` Notes: - type is usually public for room chat and private for DM room routing. - Private room names are formatted like dm:userA-userB (sorted usernames). - Moderation/system events are sent as type=system messages. ### Message Envelope (server -> client) ```json { "room": "main", "username": "viewer123", "text": "hello world", "type": "public", "timestamp": "2026-08-04T12:34:56Z" } ``` ## Admin Session API ### Login - POST /api/admin/login - Auth: none Request: ```json { "username": "admin", "password": "your-password" } ``` Success response: ```json { "authenticated": true, "username": "admin" } ``` ### Logout - POST /api/admin/logout - Auth: optional (clears cookie either way) Response: ```json { "authenticated": false } ``` ### Session Check - GET /api/admin/session - Auth: cookie required Success response: ```json { "authenticated": true, "username": "admin" } ``` Failure: - 401 unauthorized ## Admin Data API All endpoints below require admin authentication. ### Overview - GET /api/admin/overview Returns aggregate counters and runtime/system snapshot fields. ### Playlist - GET /api/admin/playlist - PUT /api/admin/playlist PUT request: ```json { "content": "file '/app/media/video1.mp4'\nfile '/app/media/video2.mp4'\n" } ``` ### Schedule - GET /api/admin/schedule Returns parsed playlist entries plus matched media metadata/runtime. ### Moderation - GET /api/admin/moderation/logs?limit=50 - POST /api/admin/moderation/actions Action request: ```json { "action": "mute", "target": "username-or-id", "moderator": "admin", "reason": "spam", "room": "main" } ``` ### Analytics - GET /api/admin/analytics Returns totals (messages, moderation actions, rooms) and top room summary. ### System - GET /api/admin/system Returns service health snapshot for chat/playout/redis/database and config metadata. ### Active Users - GET /api/admin/users Returns currently discovered presence users (room + username) when Redis is available. ## Media Library API ### Media Collection - GET /api/admin/media - POST /api/admin/media (multipart upload, legacy path) Multipart form field name: file ### Media Item - GET /api/admin/media/{id} - PUT /api/admin/media/{id} - DELETE /api/admin/media/{id} PUT request: ```json { "status": "ready", "title": "My Clip", "tags": "music,live" } ``` ## Resumable Upload API Maximum file size: 2GB Default chunk size: 8MB ### Initialize or Resume Session - POST /api/admin/media/uploads Request: ```json { "uploadId": "optional-existing-id", "fileName": "clip.mp4", "fileSize": 123456789, "mimeType": "video/mp4", "chunkSize": 8388608, "totalChunks": 15 } ``` Response: ```json { "uploadId": "hex-token", "fileName": "clip.mp4", "fileSize": 123456789, "chunkSize": 8388608, "totalChunks": 15, "uploadedChunks": 3, "progress": 20 } ``` ### Check Upload Status - GET /api/admin/media/uploads/{uploadId} Returns same status envelope as initialize. ### Upload Chunk - PUT /api/admin/media/uploads/{uploadId}/chunk?index={chunkIndex} - Content-Type: application/octet-stream Response returns updated upload status envelope. ### Complete Upload - POST /api/admin/media/uploads/{uploadId}/complete On success returns admin media listing payload (files + generatedAt). ## Metrics and Health ### Chat metrics - GET /metrics Prometheus exposition includes connected clients, stored messages, moderation actions, Redis indicators, active admin sessions, and build info. ### Health - GET /health on chat service - GET /health on playout service - GET /health on nginx reverse proxy ## HTTP Status Conventions | Code | Meaning | When | | ---- | --------------------- | ----------------------------------------------------------- | | 200 | OK | Successful GET, PUT, or action | | 201 | Created | Successful POST (upload init, override created) | | 204 | No Content | Successful DELETE, CORS preflight | | 400 | Bad Request | Malformed JSON, missing required fields, invalid input | | 401 | Unauthorized | Missing or expired admin session | | 403 | Forbidden | Valid session but insufficient permissions | | 404 | Not Found | Resource doesn't exist | | 405 | Method Not Allowed | Wrong HTTP method for endpoint | | 409 | Conflict | Schedule override overlap, upload ID mismatch | | 413 | Payload Too Large | Upload exceeds 2 GB limit | | 422 | Unprocessable | File fails normalization check (codec, fps, resolution) | | 429 | Too Many Requests | Rate limit exceeded (chat messages, WS connections, login) | | 500 | Internal Server Error | Unexpected server failure | | 503 | Service Unavailable | Redis unavailable for user actions, playout health degraded | ## Rate Limiting All limits are per-IP (from `X-Forwarded-For` or `RemoteAddr`) unless noted. | Resource | Limit | Window | Response | | --------------------- | ---------------------------------- | ---------------------------------------- | ------------------------------------------------------ | | Chat messages | `CHAT_RATE_LIMIT_MAX` (default 10) | `CHAT_RATE_LIMIT_WINDOW_S` (default 10s) | 429 + `{"type":"system","text":"Rate limit exceeded"}` | | WebSocket connections | 5 | 60s | 429 + `"too many connections from this IP"` | | Admin login | 5 | 5min | 429 + `"too many login attempts, try again later"` | Rate limit headers on applicable responses: - `X-RateLimit-Limit`: maximum requests per window - `Retry-After`: seconds until window resets - 413: payload too large - 500: internal server error