7690d959b3
CI / Security audit (push) Successful in 1m52s
Docs Check / Markdown lint (push) Successful in 1m37s
Build and Push Docker Image / build (push) Successful in 2m2s
Licence Check / Licence compatibility and copyright header verification (push) Failing after 2m33s
CI / Swagger Validation & Coverage (push) Successful in 3m17s
Docs Check / Mermaid diagram parse check (push) Successful in 3m31s
CI / Tests & coverage (push) Successful in 4m5s
Fixes the root cause of the regression from v1.7.16. The v1.7.16 fix correctly cast arrQueueId to String, but the lookup was performed against downloadClientRegistry.getAllDownloads() which returns raw download client data (qBittorrent, SABnzbd, etc.) that never has arrQueueId populated. The fix now looks up the queue record directly from the Sonarr/Radarr queue cache where record.id is the numeric queue ID, using String() casting on both sides to handle the DOM-dataset (string) vs API response (number) type difference. Resolves Gitea Issue #48 Closes #48
1949 lines
55 KiB
YAML
1949 lines
55 KiB
YAML
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.17
|
|
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: Sonarr API proxy
|
|
- name: Radarr
|
|
description: Radarr API proxy
|
|
- 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"
|
|
|
|
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'
|
|
|