5b3034e290
Docs Check / Markdown lint (push) Successful in 46s
Build and Push Docker Image / build (push) Successful in 1m40s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 2m22s
CI / Security audit (push) Successful in 2m48s
CI / Swagger Validation & Coverage (push) Successful in 3m3s
Docs Check / Mermaid diagram parse check (push) Successful in 3m9s
CI / Tests & coverage (push) Successful in 3m36s
1970 lines
56 KiB
YAML
1970 lines
56 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.23
|
|
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"
|
|
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'
|
|
|