openapi: 3.1.0 info: title: sofarr API description: | sofarr is a personal media download dashboard that aggregates download activity from SABnzbd, Sonarr, Radarr, qBittorrent, Transmission, and rTorrent, filters results by Emby/Jellyfin user identity, and presents a real-time personalized dashboard. ## Authentication sofarr uses cookie-based authentication with Emby/Jellyfin. To authenticate: 1. Call POST /api/auth/login with username and password 2. The server sets an httpOnly signed cookie named `emby_user` 3. The server also sets a `csrf_token` cookie (readable by JS) 4. Subsequent requests must include the cookies and send the `X-CSRF-Token` header for state-changing operations (POST, PUT, PATCH, DELETE) ## Rate Limiting To protect the system from resource exhaustion, rate limiters are enforced at different levels: - **General API Limiter**: Enforces a limit of **300 requests per 15 minutes** per IP across all `/api/*` endpoints. - *Exemption:* Requests starting with `/api/dashboard/cover-art` are completely exempted from this limit to avoid normal dashboard image browsing triggering blocks. - **Login Rate Limiter**: Enforces a strict limit of **10 attempts per 15 minutes** per IP on `POST /api/auth/login`. - *Exemption:* This limiter only tracks and counts *failed* login attempts (`skipSuccessfulRequests: true`). Successful logins do not count towards the lockout threshold. - **Webhook Limiter**: Enforces a limit of **60 requests per minute** per IP on stateful webhook receiver endpoints (`/api/webhook/*`). - **Health and Readiness Probes**: The public `/health` and `/ready` endpoints are mounted at the root directory level rather than under `/api/*` and are completely exempt from both rate limiting and authentication controls. ## SSE Streaming Real-time updates are available via Server-Sent Events at GET /api/dashboard/stream. version: 1.7.35 contact: name: sofarr license: name: MIT servers: - url: https://sofarr.example.com description: sofarr API (auto-detected from current URL) tags: - name: Health description: Public health check endpoints - name: Auth description: Authentication and session management - name: Dashboard description: User dashboard and download data - name: Status description: Server status and metrics (admin-only) - name: History description: Download history - name: Webhook description: Webhook receivers for Sonarr/Radarr - name: Sonarr description: Selective Sonarr API proxy (specific endpoints only) - name: Radarr description: Selective Radarr API proxy (specific endpoints only) - name: SABnzbd description: SABnzbd API proxy - name: Emby description: Emby/Jellyfin API proxy - name: Ombi description: Ombi request management security: - CookieAuth: [] components: securitySchemes: CookieAuth: type: apiKey in: cookie name: emby_user description: | httpOnly signed cookie containing user session. Set by POST /api/auth/login. Format: JSON string with {id, name, isAdmin}. Must be sent with every authenticated request along with X-CSRF-Token header for mutations. CsrfToken: type: apiKey in: header name: X-CSRF-Token description: | CSRF token from csrf_token cookie. Required for POST, PUT, PATCH, DELETE requests. Obtain via GET /api/auth/csrf or from the response of POST /api/auth/login. schemas: NormalizedDownload: type: object description: Standardized download object from any client properties: id: type: string description: Client-specific unique ID example: "abc123" title: type: string description: Download title/name example: "Show.Name.S01E01.1080p.WEB-DL" type: type: string enum: [usenet, torrent] description: Download type example: "torrent" client: type: string description: Client identifier example: "qbittorrent" instanceId: type: string description: Instance identifier example: "qbittorrent-main" instanceName: type: string description: Instance display name example: "Main qBittorrent" status: type: string description: Normalized status example: "Downloading" progress: type: number description: Progress percentage (0-100) example: 45.5 size: type: integer description: Total size in bytes example: 1073741824 downloaded: type: integer description: Downloaded bytes example: 536870912 speed: type: integer description: Current speed in bytes/sec example: 1048576 eta: type: integer nullable: true description: ETA in seconds, null if unknown example: 300 category: type: string description: Download category example: "tv" tags: type: array items: type: string description: Download tags example: ["user-john"] savePath: type: string description: Save path example: "/downloads/tv" addedOn: type: string description: Added timestamp (ISO 8601) example: "2026-05-21T10:00:00.000Z" arrQueueId: type: integer nullable: true description: Sonarr/Radarr queue ID example: 123 arrType: type: string enum: [series, movie] nullable: true description: Sonarr/Radarr type example: "series" ombiLink: type: string nullable: true description: Link to Ombi request or search example: "https://ombi.example.com/#/request/movie/123" ombiRequestId: type: string nullable: true description: Ombi request ID (if request exists) example: "123" ombiTooltip: type: string nullable: true description: Tooltip text for Ombi icon ("Request" or "Search") example: "Request" arrLink: type: string nullable: true format: uri description: Sonarr/Radarr show/movie web UI link (admin-only) example: "http://sonarr:8989/series/show-slug" downloadPath: type: string nullable: true description: Save path in download client (admin-only) example: "/downloads/series/show-slug" targetPath: type: string nullable: true description: Target path in library (admin-only) example: "/tv/show-slug" arrInstanceKey: type: string nullable: true description: Sonarr/Radarr instance API key (admin-only) example: "api-key-here" DashboardPayload: type: object properties: user: type: string description: Username example: "john" isAdmin: type: boolean description: Admin flag example: false downloads: type: array items: $ref: '#/components/schemas/NormalizedDownload' description: Matched download objects downloadClients: type: array items: type: object properties: id: type: string example: "qbittorrent-main" name: type: string example: "Main qBittorrent" type: type: string example: "qbittorrent" description: Configured download clients ErrorResponse: type: object properties: error: type: string description: Error message example: "Invalid username or password" details: type: string nullable: true description: Additional error details (dev-only) example: "Emby API returned 401" OmbiRequest: type: object description: Normalised Ombi movie or TV request properties: id: type: integer example: 123 title: type: string example: "The Batman" requestedDate: type: string format: date-time nullable: true example: "2026-05-21T10:00:00.000Z" available: type: boolean example: false approved: type: boolean example: true denied: type: boolean example: false requested: type: boolean example: true deniedReason: type: string nullable: true example: "Already on Plex" theMovieDbId: type: integer nullable: true example: 414906 year: type: integer nullable: true example: 2022 quality: type: string nullable: true example: "1080p" requestedUser: type: object nullable: true mediaType: type: string enum: [movie, tv] description: Injected by Sofarr to distinguish movies from TV example: "movie" BlocklistSearchRequest: type: object required: - arrQueueId - arrType - arrInstanceUrl - arrContentType properties: arrQueueId: type: integer description: Sonarr/Radarr queue record ID example: 123 arrType: type: string enum: [sonarr, radarr] description: Which *arr service example: "sonarr" arrInstanceUrl: type: string format: uri description: Base URL of the *arr instance example: "http://sonarr:8989" arrInstanceKey: type: string description: API key for the *arr instance. Only required for admin users; non-admin requests resolve the key from server-side configurations using arrInstanceUrl. example: "abc123def456" arrContentId: type: integer description: episodeId (Sonarr) or movieId (Radarr) example: 456 arrContentIds: type: array items: type: integer description: Array of episodeIds for multi-episode packs (Sonarr) example: [456, 457] arrSeriesId: type: integer description: seriesId for fallback automatic series search (Sonarr) example: 789 arrContentType: type: string enum: [episode, movie] description: Content type for search command example: "episode" WebhookPayload: type: object description: Webhook payload from Sonarr/Radarr properties: eventType: type: string description: Event type (Grab, Download, DownloadFolderImported, etc.) example: "Grab" instanceName: type: string description: Instance name from configuration example: "Main Sonarr" date: type: string format: date-time description: Event timestamp example: "2026-05-21T10:00:00.000Z" HistoryItem: type: object properties: type: type: string enum: [series, movie] example: "series" outcome: type: string enum: [imported, failed] example: "imported" title: type: string description: Source title from *arr record example: "Show.Name.S01E01.1080p.WEB-DL" seriesName: type: string nullable: true example: "Show Name" movieName: type: string nullable: true example: "Movie Name" coverArt: type: string nullable: true format: uri example: "http://sonarr:8989/media/poster.jpg" completedAt: type: string format: date-time example: "2026-05-21T10:00:00.000Z" quality: type: string nullable: true example: "HDTV-1080p" instanceName: type: string nullable: true example: "Main Sonarr" arrLink: type: string nullable: true format: uri example: "http://sonarr:8989/series/show-slug" allTags: type: array items: type: string example: ["user-john", "user-jane"] matchedUserTag: type: string nullable: true example: "user-john" availableForUpgrade: type: boolean description: True if failed but content is on disk example: true StatusResponse: type: object properties: server: type: object properties: uptimeSeconds: type: integer example: 3600 nodeVersion: type: string example: "v22.0.0" memoryUsageMB: type: number example: 128.5 heapUsedMB: type: number example: 64.2 heapTotalMB: type: number example: 128.0 polling: type: object properties: enabled: type: boolean example: true intervalMs: type: integer example: 5000 lastPoll: type: object additionalProperties: true cache: type: object additionalProperties: true webhooks: type: object properties: sonarr: type: object properties: configured: type: boolean example: true eventsReceived: type: integer example: 42 lastWebhookTimestamp: type: string format: date-time pollsSkipped: type: integer example: 15 radarr: type: object properties: configured: type: boolean example: true eventsReceived: type: integer example: 38 lastWebhookTimestamp: type: string format: date-time pollsSkipped: type: integer example: 12 paths: # Public health endpoints /health: get: tags: [Health] summary: Health check description: Returns server uptime and status. No authentication required. responses: '200': description: Server is healthy content: application/json: schema: type: object properties: status: type: string example: "ok" uptime: type: number example: 3600.5 /ready: get: tags: [Health] summary: Readiness check description: Checks if critical configuration (EMBY_URL) is present. Used by Docker HEALTHCHECK. responses: '200': description: Server is ready content: application/json: schema: type: object properties: status: type: string example: "ready" '503': description: Server not ready content: application/json: schema: type: object properties: status: type: string example: "not ready" reason: type: string example: "EMBY_URL not configured" # Auth endpoints (detailed in JSDoc) /api/auth/login: post: tags: [Auth] summary: Login description: Authenticate with Emby/Jellyfin and set session cookies security: [] responses: '200': description: Login successful content: application/json: schema: type: object properties: success: type: boolean example: true user: type: object properties: id: type: string name: type: string isAdmin: type: boolean csrfToken: type: string '400': description: Invalid input content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '401': description: Invalid credentials content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/auth/me: get: tags: [Auth] summary: Get current user description: Returns the currently authenticated user from the session cookie security: - CookieAuth: [] responses: '200': description: User data content: application/json: schema: type: object properties: authenticated: type: boolean user: type: object properties: id: type: string name: type: string isAdmin: type: boolean /api/auth/csrf: get: tags: [Auth] summary: Refresh CSRF token description: Get a fresh CSRF token without re-authenticating security: [] responses: '200': description: CSRF token content: application/json: schema: type: object properties: csrfToken: type: string /api/auth/logout: post: tags: [Auth] summary: Logout description: Clear session cookies and revoke Emby token security: - CookieAuth: [] - CsrfToken: [] responses: '200': description: Logout successful content: application/json: schema: type: object properties: success: type: boolean example: true # Dashboard endpoints (detailed in JSDoc) /api/dashboard/user-downloads: get: tags: [Dashboard] summary: Get user downloads (deprecated) description: DEPRECATED: Use /api/dashboard/stream for real-time updates. Returns current downloads for the authenticated user. security: - CookieAuth: [] responses: '200': description: User downloads content: application/json: schema: type: object properties: user: type: string isAdmin: type: boolean downloads: type: array items: $ref: '#/components/schemas/NormalizedDownload' /api/dashboard/cover-art: get: tags: [Dashboard] summary: Cover art proxy description: Proxies external poster images server-side for CSP compliance security: - CookieAuth: [] parameters: - name: url in: query required: true schema: type: string format: uri responses: '200': description: Image data content: image/*: schema: type: string format: binary '400': description: Invalid URL or non-image content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/dashboard/stream: get: tags: [Dashboard] summary: SSE stream description: Server-Sent Events stream for real-time download updates. No CSRF token required (GET request). security: - CookieAuth: [] responses: '200': description: SSE stream content: text/event-stream: schema: type: string /api/dashboard/blocklist-search: post: tags: [Dashboard] summary: Blocklist and re-search description: Removes queue item with blocklist=true, then triggers new automatic search. Accessible by admins, or by non-admins who own the item under specific eligibility conditions (has import issues, or torrent older than 1h and availability < 100%). security: - CookieAuth: [] - CsrfToken: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BlocklistSearchRequest' responses: '200': description: Success content: application/json: schema: type: object properties: ok: type: boolean example: true '403': description: Permission denied (admin or qualifying conditions required) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' # Status endpoint (detailed in JSDoc) /api/status/status: get: tags: [Status] summary: Get server status description: Admin-only endpoint returning server metrics, cache stats, polling info, and webhook metrics security: - CookieAuth: [] responses: '200': description: Status data content: application/json: schema: $ref: '#/components/schemas/StatusResponse' '403': description: Admin access required content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' # History endpoint (detailed in JSDoc) /api/history/recent: get: tags: [History] summary: Get recent history description: Returns Sonarr/Radarr history records for the authenticated user, filtered by tag and date security: - CookieAuth: [] parameters: - name: days in: query schema: type: integer minimum: 1 maximum: 90 default: 7 description: Number of days to look back - name: showAll in: query schema: type: boolean default: false description: Admin-only: show all users' history responses: '200': description: History items content: application/json: schema: type: object properties: user: type: string isAdmin: type: boolean days: type: integer history: type: array items: $ref: '#/components/schemas/HistoryItem' # Webhook endpoints (detailed in JSDoc) /api/webhook/sonarr: post: tags: [Webhook] summary: Sonarr webhook description: Receives webhook events from Sonarr. Requires X-Sofarr-Webhook-Secret header or secret query parameter. security: [] parameters: - name: secret in: query required: false schema: type: string description: Webhook secret token (alternative to X-Sofarr-Webhook-Secret header) requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WebhookPayload' responses: '200': description: Event received content: application/json: schema: type: object properties: received: type: boolean duplicate: type: boolean '401': description: Invalid or missing secret content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '400': description: Invalid payload content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/webhook/radarr: post: tags: [Webhook] summary: Radarr webhook description: Receives webhook events from Radarr. Requires X-Sofarr-Webhook-Secret header or secret query parameter. security: [] parameters: - name: secret in: query required: false schema: type: string description: Webhook secret token (alternative to X-Sofarr-Webhook-Secret header) requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WebhookPayload' responses: '200': description: Event received content: application/json: schema: type: object properties: received: type: boolean duplicate: type: boolean '401': description: Invalid or missing secret content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '400': description: Invalid payload content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/webhook/ombi: post: tags: [Webhook] summary: Ombi webhook description: Receives webhook events from Ombi. Requires X-Sofarr-Webhook-Secret header or secret query parameter. security: [] parameters: - name: secret in: query required: false schema: type: string description: Webhook secret token (alternative to X-Sofarr-Webhook-Secret header) requestBody: required: true content: application/json: schema: type: object responses: '200': description: Event received content: application/json: schema: type: object properties: received: type: boolean example: true '401': description: Invalid or missing secret content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '400': description: Invalid payload content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/webhook/config: get: tags: [Webhook] summary: Get webhook configuration status description: Returns whether the required webhook configuration is properly configured. security: - CookieAuth: [] responses: '200': description: Webhook configuration status content: application/json: schema: type: object properties: valid: type: boolean example: true missing: type: array items: type: string example: [] '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' # Sonarr proxy endpoints (detailed in JSDoc) /api/sonarr/queue: get: tags: [Sonarr] summary: Get Sonarr queue description: Proxy to Sonarr's queue endpoint security: - CookieAuth: [] responses: '200': description: Queue data content: application/json: schema: type: object '500': description: Proxy error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/sonarr/history: get: tags: [Sonarr] summary: Get Sonarr history description: Proxy to Sonarr's history endpoint security: - CookieAuth: [] parameters: - name: pageSize in: query schema: type: integer default: 50 responses: '200': description: History data content: application/json: schema: type: object /api/sonarr/series/{id}: get: tags: [Sonarr] summary: Get series details description: Proxy to Sonarr's series details endpoint security: - CookieAuth: [] parameters: - name: id in: path required: true schema: type: integer responses: '200': description: Series data content: application/json: schema: type: object /api/sonarr/series: get: tags: [Sonarr] summary: Get all series description: Proxy to Sonarr's series list endpoint security: - CookieAuth: [] responses: '200': description: Series list content: application/json: schema: type: array /api/sonarr/notifications: get: tags: [Sonarr] summary: List notifications description: Proxy to Sonarr's notification list endpoint security: - CookieAuth: [] responses: '200': description: Notifications list content: application/json: schema: type: array '503': description: Sonarr not configured content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/sonarr/notifications/{id}: get: tags: [Sonarr] summary: Get notification description: Proxy to Sonarr's notification details endpoint security: - CookieAuth: [] parameters: - name: id in: path required: true schema: type: integer responses: '200': description: Notification data content: application/json: schema: type: object /api/sonarr/notifications: post: tags: [Sonarr] summary: Create notification description: Proxy to Sonarr's notification create endpoint security: - CookieAuth: [] - CsrfToken: [] requestBody: required: true content: application/json: schema: type: object responses: '200': description: Created notification content: application/json: schema: type: object /api/sonarr/notifications/{id}: put: tags: [Sonarr] summary: Update notification description: Proxy to Sonarr's notification update endpoint security: - CookieAuth: [] - CsrfToken: [] parameters: - name: id in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: type: object responses: '200': description: Updated notification content: application/json: schema: type: object /api/sonarr/notifications/{id}: delete: tags: [Sonarr] summary: Delete notification description: Proxy to Sonarr's notification delete endpoint security: - CookieAuth: [] - CsrfToken: [] parameters: - name: id in: path required: true schema: type: integer responses: '200': description: Deleted notification content: application/json: schema: type: object /api/sonarr/notifications/test: post: tags: [Sonarr] summary: Test notification description: Proxy to Sonarr's notification test endpoint security: - CookieAuth: [] - CsrfToken: [] requestBody: required: true content: application/json: schema: type: object responses: '200': description: Test result content: application/json: schema: type: object /api/sonarr/notifications/schema: get: tags: [Sonarr] summary: Get notification schema description: Proxy to Sonarr's notification schema endpoint security: - CookieAuth: [] responses: '200': description: Schema data content: application/json: schema: type: object /api/sonarr/notifications/sofarr-webhook: post: tags: [Sonarr] summary: Configure Sofarr webhook description: One-click setup for Sofarr webhook notification in Sonarr security: - CookieAuth: [] - CsrfToken: [] responses: '200': description: Configured notification content: application/json: schema: type: object '400': description: Missing configuration content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '503': description: Sonarr not configured content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' # Radarr proxy endpoints (detailed in JSDoc) /api/radarr/queue: get: tags: [Radarr] summary: Get Radarr queue description: Proxy to Radarr's queue endpoint security: - CookieAuth: [] responses: '200': description: Queue data content: application/json: schema: type: object /api/radarr/history: get: tags: [Radarr] summary: Get Radarr history description: Proxy to Radarr's history endpoint security: - CookieAuth: [] parameters: - name: pageSize in: query schema: type: integer default: 50 responses: '200': description: History data content: application/json: schema: type: object /api/radarr/movies/{id}: get: tags: [Radarr] summary: Get movie details description: Proxy to Radarr's movie details endpoint security: - CookieAuth: [] parameters: - name: id in: path required: true schema: type: integer responses: '200': description: Movie data content: application/json: schema: type: object /api/radarr/movies: get: tags: [Radarr] summary: Get all movies description: Proxy to Radarr's movie list endpoint security: - CookieAuth: [] responses: '200': description: Movies list content: application/json: schema: type: array /api/radarr/notifications: get: tags: [Radarr] summary: List notifications description: Proxy to Radarr's notification list endpoint security: - CookieAuth: [] responses: '200': description: Notifications list content: application/json: schema: type: array '503': description: Radarr not configured content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/radarr/notifications/{id}: get: tags: [Radarr] summary: Get notification description: Proxy to Radarr's notification details endpoint security: - CookieAuth: [] parameters: - name: id in: path required: true schema: type: integer responses: '200': description: Notification data content: application/json: schema: type: object /api/radarr/notifications: post: tags: [Radarr] summary: Create notification description: Proxy to Radarr's notification create endpoint security: - CookieAuth: [] - CsrfToken: [] requestBody: required: true content: application/json: schema: type: object responses: '200': description: Created notification content: application/json: schema: type: object /api/radarr/notifications/{id}: put: tags: [Radarr] summary: Update notification description: Proxy to Radarr's notification update endpoint security: - CookieAuth: [] - CsrfToken: [] parameters: - name: id in: path required: true schema: type: integer requestBody: required: true content: application/json: schema: type: object responses: '200': description: Updated notification content: application/json: schema: type: object /api/radarr/notifications/{id}: delete: tags: [Radarr] summary: Delete notification description: Proxy to Radarr's notification delete endpoint security: - CookieAuth: [] - CsrfToken: [] parameters: - name: id in: path required: true schema: type: integer responses: '200': description: Deleted notification content: application/json: schema: type: object /api/radarr/notifications/test: post: tags: [Radarr] summary: Test notification description: Proxy to Radarr's notification test endpoint security: - CookieAuth: [] - CsrfToken: [] requestBody: required: true content: application/json: schema: type: object responses: '200': description: Test result content: application/json: schema: type: object /api/radarr/notifications/schema: get: tags: [Radarr] summary: Get notification schema description: Proxy to Radarr's notification schema endpoint security: - CookieAuth: [] responses: '200': description: Schema data content: application/json: schema: type: object /api/radarr/notifications/sofarr-webhook: post: tags: [Radarr] summary: Configure Sofarr webhook description: One-click setup for Sofarr webhook notification in Radarr security: - CookieAuth: [] - CsrfToken: [] responses: '200': description: Configured notification content: application/json: schema: type: object '400': description: Missing configuration content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '503': description: Radarr not configured content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' # SABnzbd proxy endpoints (detailed in JSDoc) /api/sabnzbd/queue: get: tags: [SABnzbd] summary: Get SABnzbd queue description: Proxy to SABnzbd's queue endpoint security: - CookieAuth: [] responses: '200': description: Queue data content: application/json: schema: type: object /api/sabnzbd/history: get: tags: [SABnzbd] summary: Get SABnzbd history description: Proxy to SABnzbd's history endpoint security: - CookieAuth: [] parameters: - name: limit in: query schema: type: integer default: 50 responses: '200': description: History data content: application/json: schema: type: object # Emby proxy endpoints (detailed in JSDoc) /api/emby/sessions: get: tags: [Emby] summary: Get active sessions description: Proxy to Emby's sessions endpoint security: - CookieAuth: [] responses: '200': description: Sessions data content: application/json: schema: type: array /api/emby/users/{id}: get: tags: [Emby] summary: Get user by ID description: Proxy to Emby's user details endpoint security: - CookieAuth: [] parameters: - name: id in: path required: true schema: type: string responses: '200': description: User data content: application/json: schema: type: object /api/emby/users: get: tags: [Emby] summary: Get all users description: Proxy to Emby's users list endpoint security: - CookieAuth: [] responses: '200': description: Users list content: application/json: schema: type: array /api/emby/session/{sessionId}/user: get: tags: [Emby] summary: Get user from session description: Get user details for a specific session ID security: - CookieAuth: [] parameters: - name: sessionId in: path required: true schema: type: string responses: '200': description: User data content: application/json: schema: type: object '404': description: Session not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' # Ombi endpoints /api/ombi/requests: get: tags: [Ombi] summary: Get Ombi requests description: Returns Ombi movie and TV requests. Non-admin users only see their own requests, while admins see all requests. Supports server-side filtering by media type, request status, title search, and sorting. security: - CookieAuth: [] parameters: - name: type in: query schema: type: array items: type: string enum: [movie, tv, all] default: [all] description: Filter by media type. Omit or use `all` for both. style: form explode: true - name: status in: query schema: type: array items: type: string enum: [pending, approved, available, denied] description: Filter by request status. Omit for all statuses. style: form explode: true - name: sort in: query schema: type: string enum: [requestedDate_desc, requestedDate_asc, title_asc, title_desc] default: requestedDate_desc description: Sort mode. - name: search in: query schema: type: string description: Case-insensitive substring match on title. - name: showAll in: query schema: type: string enum: ['true', 'false'] description: Admin only. Show all users' requests. responses: '200': description: Ombi requests retrieved successfully content: application/json: schema: type: object properties: user: type: string isAdmin: type: boolean showAll: type: boolean requests: type: object properties: movie: type: array items: $ref: '#/components/schemas/OmbiRequest' tv: type: array items: $ref: '#/components/schemas/OmbiRequest' total: type: integer '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/ombi/webhook/enable: post: tags: [Ombi] summary: Enable Ombi webhook description: Registers or updates the Sofarr webhook in Ombi. Requires authentication and CSRF protection. security: - CookieAuth: [] responses: '200': description: Webhook enabled successfully content: application/json: schema: type: object properties: success: type: boolean webhookUrl: type: string applicationToken: type: string '400': description: Invalid request or missing configuration content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/ombi/webhook/status: get: tags: [Ombi] summary: Get Ombi webhook status description: Returns the current Ombi webhook configuration status and metrics. security: - CookieAuth: [] responses: '200': description: Webhook status retrieved successfully content: application/json: schema: type: object properties: enabled: type: boolean webhookUrl: type: string nullable: true applicationToken: type: string nullable: true triggers: type: object properties: requestAvailable: type: boolean requestApproved: type: boolean requestDeclined: type: boolean requestPending: type: boolean requestProcessing: type: boolean stats: type: object nullable: true properties: eventsReceived: type: integer pollsSkipped: type: integer lastWebhookTimestamp: type: integer '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/ombi/webhook/test: post: tags: [Ombi] summary: Test Ombi webhook description: Sends a test webhook event to the Sofarr Ombi webhook endpoint. security: - CookieAuth: [] - CsrfToken: [] responses: '200': description: Test webhook sent successfully content: application/json: schema: type: object properties: success: type: boolean '400': description: Invalid request or missing configuration content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/debug/status: get: tags: [Debug] summary: Check if log streaming is enabled description: Returns whether the log streaming feature is enabled at runtime. No authentication required. security: [] responses: '200': description: Feature status returned successfully content: application/json: schema: type: object properties: enabled: type: boolean example: true /api/debug/server-logs: get: tags: [Debug] summary: Stream server logs in real-time description: | Streams server-side standard output (stdout/stderr) logs via Server-Sent Events (SSE). **Security Policies:** - **Subnet IP Filtering**: IP must match subnet ranges configured in `LOG_ALLOW_SUBNETS` (if set). - **Bypass Header**: Direct bypass if `X-Webhook-Secret` header matches the configured `SOFARR_WEBHOOK_SECRET`. - **Session Auth**: Emby session cookie `emby_user` where user is an administrator. - **Basic Auth Fallback**: `Authorization` header containing credentials of a valid Emby administrator. security: - CookieAuth: [] parameters: - name: X-Webhook-Secret in: header required: false schema: type: string description: Fast-track webhook secret bypass token responses: '200': description: Event stream established content: text/event-stream: schema: type: string '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden (feature disabled or IP not in subnet allowlist) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /api/debug/client-logs: get: tags: [Debug] summary: Stream client console logs in real-time description: | Streams client-side console logs via Server-Sent Events (SSE). **Security Policies:** - **Subnet IP Filtering**: IP must match subnet ranges configured in `LOG_ALLOW_SUBNETS` (if set). - **Bypass Header**: Direct bypass if `X-Webhook-Secret` header matches the configured `SOFARR_WEBHOOK_SECRET`. - **Session Auth**: Emby session cookie `emby_user` where user is an administrator. - **Basic Auth Fallback**: `Authorization` header containing credentials of a valid Emby administrator. security: - CookieAuth: [] parameters: - name: X-Webhook-Secret in: header required: false schema: type: string description: Fast-track webhook secret bypass token responses: '200': description: Event stream established content: text/event-stream: schema: type: string '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden (feature disabled or IP not in subnet allowlist) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' post: tags: [Debug] summary: Ingest client console logs description: | Ingests a batch of client-side console logs into the server-side rolling clientLogBuffer. **Security Policies:** - **Subnet IP Filtering**: IP must match subnet ranges configured in `LOG_ALLOW_SUBNETS` (if set). - **Bypass Header**: Direct bypass if `X-Webhook-Secret` header matches the configured `SOFARR_WEBHOOK_SECRET`. - **Session Auth**: Emby session cookie `emby_user` where user is an administrator. - **Basic Auth Fallback**: `Authorization` header containing credentials of a valid Emby administrator. security: - CookieAuth: [] parameters: - name: X-Webhook-Secret in: header required: false schema: type: string description: Fast-track webhook secret bypass token requestBody: required: true content: application/json: schema: type: array items: type: object required: [level, message] properties: timestamp: type: string format: date-time level: type: string enum: [info, warn, error] message: type: string responses: '200': description: Logs ingested successfully content: application/json: schema: type: object properties: success: type: boolean count: type: integer '400': description: Invalid JSON body content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden (feature disabled or IP not in subnet allowlist) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse'