### GET /api/collections/media response example Source: https://docs.maintainerr.info/api/collections Example JSON response for the GET /api/collections/media endpoint, showing a raw membership row with fields like id, collectionId, mediaServerId, tmdbId, addDate, image_path, isManual, includedByRule, manualMembershipSource, sizeBytes, and ruleEvaluationFailed. ```json [ { "id": 1, "collectionId": 1, "mediaServerId": "12345", "tmdbId": 550, "addDate": "2026-06-05T00:00:00.000Z", "image_path": null, "isManual": true, "includedByRule": false, "manualMembershipSource": "local", "sizeBytes": null, "ruleEvaluationFailed": false } ] ``` -------------------------------- ### Start local development server with npm start Source: https://docs.maintainerr.info/contributing Starts the local development server for the documentation site. ```bash npm start ``` -------------------------------- ### GET /api/notifications/configurations - Response Source: https://docs.maintainerr.info/api/notifications Example JSON response from the GET endpoint. Returns an array of configurations; an empty array means no agents are configured. Secrets are returned in cleartext. ```json [ { "id": 1, "name": "My Discord", "agent": "discord", "enabled": true, "types": [2, 4], "options": { "agent": "discord", "webhookUrl": "https://discord.com/api/webhooks/..." }, "aboutScale": 3 } ] ``` -------------------------------- ### Install dependencies with npm install Source: https://docs.maintainerr.info/contributing Run this command in the project root to install all required dependencies for the documentation site. ```bash npm install ``` -------------------------------- ### GET /api/media-server/meta/{id}/maintainerr-status - Response Example Source: https://docs.maintainerr.info/api/media-server Example JSON response explaining why an item is excluded from or manually added to Maintainerr collections. A 'Global' entry with no targetPath means a global exclusion. The '(5d left)' suffix is the remaining countdown, computed at request time. ```json { "excludedFrom": [ { "label": "Global" }, { "label": "Example collection", "targetPath": "/collections/1/exclusions" } ], "manuallyAddedTo": [ { "label": "Example collection (5d left)", "targetPath": "/collections/1" } ] } ``` -------------------------------- ### GET /api/overlays/settings Source: https://docs.maintainerr.info/api/overlays Returns the current overlay settings, including enabled flag, cron schedule, and legacy style blocks. On a fresh install, it creates default settings before responding. ```APIDOC ## GET /api/overlays/settings ### Description Returns the overlay settings. On a fresh install, if the settings row does not exist, it is created from defaults before the response is built. ### Method GET ### Endpoint /api/overlays/settings ### Response #### Success Response (200) - **enabled** (boolean) - Whether overlays are enabled. - **cronSchedule** (string) - The cron schedule for overlay generation. - **posterOverlayText** (object) - Legacy style block. - **posterOverlayStyle** (object) - Legacy style block. - **posterFrame** (object) - Legacy style block. - **titleCardOverlayText** (object) - Legacy style block. - **titleCardOverlayStyle** (object) - Legacy style block. - **titleCardFrame** (object) - Legacy style block. #### Response Example { "enabled": true, "cronSchedule": "0 4 * * *", "posterOverlayText": {}, "posterOverlayStyle": {}, "posterFrame": {}, "titleCardOverlayText": {}, "titleCardOverlayStyle": {}, "titleCardFrame": {} } #### Error Responses - **500** - The read, or the first-run write described below, failed. ``` -------------------------------- ### Radarr disk space response example Source: https://docs.maintainerr.info/api/servarr Example JSON response from GET /api/servarr/radarr/{id}/diskspace. Shows a merged mount list with fields id, path, label, freeSpace, totalSpace, and hasAccurateTotalSpace. Entries from root folders have label null and hasAccurateTotalSpace false. ```json [ { "id": 1, "path": "/movies", "label": "/", "freeSpace": 0, "totalSpace": 0, "hasAccurateTotalSpace": true } ] ``` -------------------------------- ### Response JSON for GET /api/storage-metrics/library-sizes Source: https://docs.maintainerr.info/api/metadata-and-storage Example response showing the generatedAt timestamp and a sizeBytesByLibrary map with library IDs as keys. Keys are media-server library ids; Plex and Jellyfin set an entry for every library (possibly 0), while Emby only includes libraries with a total above zero. ```json { "generatedAt": "2026-06-05T12:00:00.000Z", "sizeBytesByLibrary": { "1": 0, "2": 0 } } ``` -------------------------------- ### GET /api/logs/settings response example Source: https://docs.maintainerr.info/api/logs Returns the saved log level, rotation size, and backup count. The response includes level, max_size (in MB), and max_files (retention count). ```json { "level": "info", "max_size": 20, "max_files": 7 } ``` -------------------------------- ### Start Docker Compose services Source: https://docs.maintainerr.info/installation Starts all services defined in the docker-compose.yml file. Run from the directory containing the file. ```bash docker compose up -d ``` -------------------------------- ### Start Maintainerr with Docker Compose Source: https://docs.maintainerr.info/downgrade Pull the pinned image and start the container using Docker Compose. ```bash docker compose pull docker compose up -d ``` -------------------------------- ### Create data directory and start Podman Quadlet unit Source: https://docs.maintainerr.info/installation Creates the data directory, reloads systemd, starts the maintainerr unit, and enables linger to keep it running while logged out. ```bash mkdir -p ~/maintainerr/data ``` ```bash systemctl --user daemon-reload ``` ```bash systemctl --user start maintainerr ``` ```bash loginctl enable-linger "$USER" # keeps it running while you are logged out ``` -------------------------------- ### GET /api/media-server/meta/{id} - Response Example Source: https://docs.maintainerr.info/api/media-server Example JSON response for fetching full metadata for a single media item. The 'path' field is the server's filesystem path for the item; movies and episodes return the media file, while shows and seasons return the folder. Watch fields (viewCount, lastViewedAt, userRating) are per-user and cached for 5 minutes. ```json { "id": "12345", "title": "An example title", "guid": "plex://movie/abc", "type": "movie", "addedAt": "2026-01-01T00:00:00.000Z", "providerIds": { "tmdb": ["550"] }, "mediaSources": [{ "id": "1", "sizeBytes": 0 }], "path": "/media/movies/An example title (2026)/An example title (2026).mkv", "library": { "id": "1", "title": "Movies" } } ``` -------------------------------- ### GET /api/settings/test/setup Source: https://docs.maintainerr.info/api/settings Reports whether the required media server settings are filled in. Returns a boolean; no connection attempt is made. ```APIDOC ## [GET] /api/settings/test/setup ### Description Reports whether the required media server settings are filled in. If the fields for the configured media server are populated, returns true; otherwise returns false. The response is a bare boolean and no connection attempt is made. ### Method GET ### Endpoint /api/settings/test/setup ### Parameters None ### Response #### Success Response (200) Returns a bare boolean: true if setup is complete (fields are populated), false otherwise. Fields required: Plex (hostname, name, port, token), Jellyfin/Emby (URL, API key). User id is optional for Jellyfin/Emby. Any internal error is reported as false. #### Response Example `true` ``` -------------------------------- ### GET /api/media-server/meta/{id}/seen - Response Example Source: https://docs.maintainerr.info/api/media-server Example JSON response for listing completed watch records for one item, aggregated across users. The route fails closed on purpose: returning [] on failure would look like 'never watched' and could lead to deletion. What counts as 'completed' is server-defined. ```json [ { "userId": "1", "itemId": "12345", "watchedAt": "2026-01-01T00:00:00.000Z", "progress": 100 } ] ``` -------------------------------- ### GET /api/settings/telemetry/status response example Source: https://docs.maintainerr.info/api/settings Example JSON response from the telemetry status endpoint. The fields nextSendAtWeekly and nextSendAtRich are null when reporting is off or when the scheduled job failed to register. ```json { "forcedOff": false, "nextSendAtWeekly": "2026-06-08T04:12:00.000Z", "nextSendAtRich": "2026-09-14T04:12:00.000Z" } ``` -------------------------------- ### GET /api/notifications/agents - Response Source: https://docs.maintainerr.info/api/notifications Example response from the agents endpoint. Lists a fixed set of supported agents and their option fields. The route always returns 200 and cannot fail. ```json [ { "name": "discord", "friendlyName": "Discord", "options": [ { "field": "webhookUrl", "type": "text", "required": true, "extraInfo": "" }, { "field": "botUsername", "type": "text", "required": false, "extraInfo": "" }, { "field": "botAvatarUrl", "type": "text", "required": false, "extraInfo": "" } ] } ] ``` -------------------------------- ### GET /api/streamystats/info response Source: https://docs.maintainerr.info/api/streamystats Example JSON response from the /api/streamystats/info endpoint. The url is the saved value exactly as stored, and serverId is null when no Streamystats server matches the Jellyfin instance. ```json { "url": "https://streamystats.example.com", "serverId": 1 } ``` -------------------------------- ### POST /api/overlays/process Source: https://docs.maintainerr.info/api/overlays Starts a full overlay run across every overlay-enabled collection. This is a fire-and-forget operation; the request returns immediately with a 202 status. ```APIDOC ## POST /api/overlays/process ### Description Start a full overlay run across every overlay-enabled collection. ### Method POST ### Endpoint /api/overlays/process ### Parameters #### Request Body - **force** (boolean) - Optional, defaults to `false`. Setting it re-renders every item even when nothing changed. ### Request Example ```json { "force": true } ``` ### Response #### Success Response (202) - The run was started. The body is empty. #### Error Responses - **400** - `force` is not a boolean. - **409** - An overlay run is already in progress. ### Notes - Destructive to artwork: uploads re-rendered images over item posters/stills and re-uploads saved originals for items that dropped out of coverage. - Reversible in principle because every original is backed up first. - On Plex, nothing is deleted when a new poster is uploaded, so items accumulate uploaded posters over time. - Only collections that delete on a schedule (with `deleteAfterDays` set and a delete-style action) are targeted. Collections with unmonitor-only or no action have their previous overlays reverted. - The run outlives the request; poll `GET /api/overlays/status`. - Silent failure modes: if native image libraries are unavailable, every item counts as an error, nothing is uploaded, and the backup is deleted. On Plex, selecting the newly uploaded poster can fail without being reported. - A run that cannot take the overlay lock is silently skipped after the 202 was already sent. ``` -------------------------------- ### List rotated log files response Source: https://docs.maintainerr.info/api/logs Example JSON response from GET /api/logs/files showing an array of log file objects with name and size in bytes. Order is ascending by date. ```json [ { "name": "maintainerr-2026-06-05.log", "size": 20480 }, { "name": "maintainerr-2026-06-04.log.gz", "size": 2048 } ] ``` -------------------------------- ### GET /api/media-server Source: https://docs.maintainerr.info/api/media-server Report the configured media server's identity and version, or nothing when it is unreachable. ```APIDOC ## GET /api/media-server ### Description Report the configured media server's identity and version, or nothing when it is unreachable. ### Method GET ### Endpoint /api/media-server ### Response #### Success Response (200) - **machineId** (string) - The machine identifier of the media server. - **version** (string) - The version of the media server. - **name** (string) - The name of the media server (Jellyfin and Emby only). - **platform** (string) - The platform of the media server (Jellyfin and Emby only). - **url** (string) - The URL of the media server (Jellyfin and Emby only). #### Response Example ```json { "machineId": "abc123", "version": "1.40.0", "name": "My server", "platform": "Linux", "url": "http://jellyfin.example.com" } ``` ### Error Handling - **200** - Status returned, or an empty body when the server is configured but unreachable. An empty body means the server is down, not that no server is configured. That case is the 403. - **403** - No server is configured. ### Notes Successful status reads are cached for 60 seconds on Jellyfin and Emby. A server that just came back is visible immediately, but one that just went down can still report fine for up to a minute. ``` -------------------------------- ### GET /api/rules/constants Source: https://docs.maintainerr.info/api/rules Returns the catalogue of applications and their comparable properties, used to fill the rule editor's dropdowns. Applications that the install cannot use are filtered out based on saved settings only; reachability is never probed. ```APIDOC ## GET /api/rules/constants ### Description Returns the catalogue of applications and their comparable properties, used to fill the rule editor's dropdowns. Applications that the install cannot use are filtered out based on saved settings only; reachability is never probed. ### Method GET ### Endpoint /api/rules/constants ### Response #### Success Response (200) - **applications** (array) - List of applications. Each application has `id`, `name`, `mediaType`, and `props`. Each property has `id`, `name`, `humanName`, `mediaType`, a `type` describing the comparisons it supports, and optionally `showType`. #### Response Example { "applications": [ { "id": "string", "name": "string", "mediaType": "string", "props": [ { "id": "string", "name": "string", "humanName": "string", "mediaType": "string", "type": "string", "showType": "string" } ] } ] } #### Error Response (500) - **Cause**: The settings read failed. ``` -------------------------------- ### POST /api/logs/client-error response example Source: https://docs.maintainerr.info/api/logs Example success response from the client error endpoint. The endpoint always returns a 201 status for any body, including an empty one. ```json { "status": "OK", "code": 1, "message": "Logged" } ``` -------------------------------- ### GET /api/media-server/libraries Source: https://docs.maintainerr.info/api/media-server List the movie and show libraries on the configured media server. ```APIDOC ## GET /api/media-server/libraries ### Description List the movie and show libraries on the configured media server. ### Method GET ### Endpoint /api/media-server/libraries ### Response #### Success Response (200) - **id** (string) - The library identifier. - **title** (string) - The library title. - **type** (string) - The library type (e.g., `movie`). - **agent** (string) - The agent used (Plex only). #### Response Example ```json [ { "id": "1", "title": "Movies", "type": "movie", "agent": "tv.plex.agents.movie" } ] ``` ### Error Handling - **200** - The library list, possibly `[]` when the server is reachable and genuinely has no movie or show libraries. - **503** - The list came back empty **and** the server could not be reached, reported as `Media server is configured but unreachable. Library list unavailable.` ### Notes Telling "empty" apart from "unreachable" is the whole point of this route, so do not read `[]` as a failure. The list is cached for 30 minutes on Jellyfin and Emby, so a newly added library can take that long to appear. ``` -------------------------------- ### GET /api/health/ready Source: https://docs.maintainerr.info/installation Readiness probe that runs a database SELECT 1 check and returns 200 when the database is reachable or 503 when it is not. ```APIDOC ## GET /api/health/ready ### Description Readiness probe that runs a database SELECT 1 check and returns 200 when the database is reachable or 503 when it is not. ### Method GET ### Endpoint /api/health/ready ### Response #### Success Response (200) - **status** (integer) - HTTP status code, 200 when the database is reachable. #### Error Response (503) - **status** (integer) - HTTP status code, 503 when the database is not reachable. #### Response Example { "status": 200 } ``` -------------------------------- ### GET /api/settings/version Source: https://docs.maintainerr.info/api/settings Returns the running application version string as a bare string, not JSON. ```APIDOC ## GET /api/settings/version ### Description Returns the running application version string. The response is a bare string, not JSON. The value comes only from the environment. A process started without npm reports `0.0.0`, so this is not a reliable build identifier. Use `GET /api/app/status` for a richer version payload. ### Method GET ### Endpoint /api/settings/version ### Response #### Success Response (200) - **body** (string) - The version string #### Response Example (No explicit example given; response is a bare string like "1.2.3".) ### Error Handling | Status | Cause | |--------|-------| | 200 | Always | ``` -------------------------------- ### GET /api/settings/tautulli Source: https://docs.maintainerr.info/api/settings Returns the stored Tautulli URL and API key. ```APIDOC ## GET /api/settings/tautulli\n\n### Description\nReturn the stored Tautulli URL and API key.\n\n### Method\nGET\n\n### Endpoint\n/api/settings/tautulli\n\n### Response\n#### Success Response (200)\n- **status** (string) - Optional. If the read failed, `status` is `NOK`.\n- **Other fields** - The stored settings, with the API key in cleartext.\n\n#### Success Response (200 with status: "NOK")\n- **status** (string) - `NOK`.\n ``` -------------------------------- ### GET /api/metadata/overview/{type} Source: https://docs.maintainerr.info/api/metadata-and-storage Fetches a provider description for a movie or show item, used when the media server has no summary. ```APIDOC ## GET /api/metadata/overview/{type} ### Description Fetches a provider description for an item, for use where the media server has none. The UI only calls this when the media server itself returned no summary. ### Method GET ### Endpoint /api/metadata/overview/{type} ### Parameters #### Path Parameters - **type** (string) - Required - `movie` or `show`. Anything else is a `400`. #### Query Parameters - **itemId** (string) - Optional - Media-server item id. Enables season and episode specific descriptions. - **tmdbId** (string) - Optional - Provider id. At least one `*Id` key is required. - **tvdbId** (string) - Optional - Provider id. At least one `*Id` key is required. - **imdbId** (string) - Optional - Provider id. At least one `*Id` key is required. - **sportarrId** (string) - Optional - Provider id. At least one `*Id` key is required. ### Response #### Success Response (200) - **overview** (string) - A description of the item. The body is empty when there is no description at all. #### Response Example { "overview": "A description of the item." } ### Error Handling - **400** - `type` is not `movie` or `show`, and at least one `*Id` was supplied. ``` -------------------------------- ### PUT /api/overlays/settings - Example JSON body Source: https://docs.maintainerr.info/api/overlays Example request body for updating overlay settings. All fields are optional; omitted fields keep their stored value. Sending enabled or cronSchedule retimes the overlay job. An empty body {} returns a 500 error, and cronSchedule is stored without validation, so an invalid expression can stop the job permanently. ```json { "enabled": true, "cronSchedule": "0 4 * * *" } ``` -------------------------------- ### GET /api/settings/download-client Source: https://docs.maintainerr.info/api/settings Return the stored download client connection and cleanup options. The password is returned in cleartext. ```APIDOC ## GET /api/settings/download-client ### Description Return the stored download client connection and cleanup options. ### Method GET ### Endpoint /api/settings/download-client ### Response #### Success Response (200) - **download_client_type** (string|null) - The client type, or null if unconfigured. - **download_client_url** (string) - The client URL. - **download_client_username** (string) - The username. - **download_client_password** (string) - The password in cleartext. - **download_client_delete_data** (boolean) - Whether to delete data on removal. - **download_client_fallback_ratio** (number) - The fallback seeding ratio. #### Response Example { "download_client_type": "qbittorrent", "download_client_url": "http://qbittorrent:8080", "download_client_username": "user", "download_client_password": "...", "download_client_delete_data": true, "download_client_fallback_ratio": 0.5 } ``` -------------------------------- ### GET /api/overlays/images/{name} Source: https://docs.maintainerr.info/api/overlays Serves a single image asset by filename. Unsafe or unsupported names are rejected with a 400 status. ```APIDOC ## GET /api/overlays/images/{name} ### Description Serves a single image asset by filename. The `400` and `404` split is deliberate: an unsafe or unsupported name never reaches the filesystem. ### Method GET ### Endpoint /api/overlays/images/{name} ### Parameters #### Path Parameters - **name** (string) - Required - A bare filename ending in `.png`, `.jpg`, `.jpeg` or `.webp`. ### Response #### Success Response (200) - The image, sent with `Cache-Control: no-cache`. ### Error Handling - **400** - `Invalid image name` or `Unsupported image type`. - **404** - `Image not found`. ``` -------------------------------- ### GET /api/settings/database/download Source: https://docs.maintainerr.info/api Streams the entire database with every secret in plaintext. ```APIDOC ## GET /api/settings/database/download ### Description Streams the entire database with every secret in plaintext. ### Method GET ### Endpoint /api/settings/database/download ### Response #### Success Response (200) - **body** (binary) - The database file stream. #### Response Example (binary stream) ``` -------------------------------- ### GET /api/settings/api/generate Source: https://docs.maintainerr.info/api/settings Generates a fresh Maintainerr API key string without storing it. Returns a bare base64 string. ```APIDOC ## GET /api/settings/api/generate ### Description Generates a fresh Maintainerr API key string without storing it. The response is a bare base64 string. This does **not** save the key. To store it, send the value back as `apikey` through `PATCH /api/settings`. Nothing server-side validates an inbound API key; the key exists so Maintainerr's own internal client can call its own API. ### Method GET ### Endpoint /api/settings/api/generate ### Response #### Success Response (200) - **body** (string) - The generated base64 API key string #### Response Example (No explicit example given; response is a bare base64 string.) ### Error Handling | Status | Cause | |--------|-------| | 200 | Always | ``` -------------------------------- ### GET /api/overlays/fonts/{name} Source: https://docs.maintainerr.info/api/overlays Serves a single font file by filename. Path traversal is rejected with a 400 status. ```APIDOC ## GET /api/overlays/fonts/{name} ### Description Serves a single font file by filename. Uploaded fonts shadow bundled ones of the same name. ### Method GET ### Endpoint /api/overlays/fonts/{name} ### Parameters #### Path Parameters - **name** (string) - Required - A bare filename such as `Inter-Bold.ttf`. Letters, digits, dot, dash and underscore only. ### Response #### Success Response (200) - The font file, cached by the browser for an hour. ### Error Handling - **400** - `Invalid font name`, which is how path traversal is rejected. - **404** - `Font not found`, including a file that exists with an unsupported extension. ### Notes Because the response is cached for an hour, add a changing query parameter after re-uploading the same filename to defeat the cache. ``` -------------------------------- ### GET /api/settings Source: https://docs.maintainerr.info/api Returns settings with ten secret fields masked. Leaves apikey and download_client_username in the clear. ```APIDOC ## GET /api/settings ### Description Returns settings with ten secret fields masked. Leaves apikey and download_client_username in the clear. ### Method GET ### Endpoint /api/settings ### Response #### Success Response (200) - **settings** (object) - The settings object with masked secret fields. - **apikey** (string) - The API key (not masked). - **download_client_username** (string) - The download client username (not masked). #### Response Example { "settings": {}, "apikey": "...", "download_client_username": "..." } ``` -------------------------------- ### GET /api/settings/api/generate Source: https://docs.maintainerr.info/api Mints an API key. Note that nothing server-side validates an inbound X-Api-Key; the key exists only for Maintainerr's internal client. ```APIDOC ## GET /api/settings/api/generate ### Description Mints an API key. The key is not validated by the server; it exists only for Maintainerr's internal client. ### Method GET ### Endpoint /api/settings/api/generate ### Response #### Success Response (200) - **apikey** (string) - The generated API key. #### Response Example { "apikey": "generated-key" } ``` -------------------------------- ### GET /api/overlays/images Source: https://docs.maintainerr.info/api/overlays Lists the overlay image assets. Only .png, .jpg, .jpeg, and .webp files are listed from the uploaded image directory. ```APIDOC ## GET /api/overlays/images ### Description Lists the overlay image assets. The response is the same `name` and `path` shape as the font list. Only `.png`, `.jpg`, `.jpeg` and `.webp` are listed, and only from the uploaded image directory. There are no bundled images. ### Method GET ### Endpoint /api/overlays/images ### Response #### Success Response (200) - **name** (string) - The image file name. - **path** (string) - The full path to the image file. #### Response Example ``` [ { "name": "poster.png", "path": "/opt/data/overlays/images/poster.png" } ] ``` ### Error Handling - **500** - The directory exists but could not be read. ``` -------------------------------- ### GET /api/rules/community/karma/history Source: https://docs.maintainerr.info/api/rules Lists which community rules this install has already voted on. Returns an array of objects with the rule ID and community rule ID. ```APIDOC ## GET /api/rules/community/karma/history ### Description List which community rules this install has already voted on. ### Method GET ### Endpoint /api/rules/community/karma/history ### Response #### Success Response (200) - **id** (number) - The ID of the vote record. - **community_rule_id** (number) - The ID of the community rule that was voted on. #### Response Example ```json [ { "id": 1, "community_rule_id": 12 } ] ``` #### Error Response (500) - The database read failed. ``` -------------------------------- ### GET /api/media-server/overview/bootstrap Source: https://docs.maintainerr.info/api/media-server Fetches the library list plus the first library's first content page in one request. A convenience route for the Overview page, so it can render in one round trip. ```APIDOC ## GET /api/media-server/overview/bootstrap ### Description Fetches the library list plus the first library's first content page in one request. A convenience route for the Overview page, so it can render in one round trip. ### Method GET ### Endpoint /api/media-server/overview/bootstrap ### Parameters #### Query Parameters - **limit** (integer) - Optional - Page size for the embedded content page, default `50` - **sort** (string) - Optional - Same values as the content route - **sortOrder** (string) - Optional - `asc` or `desc` ### Response #### Success Response (200) - **libraries** (array) - List of libraries, each with `id`, `title`, `type` - **selectedLibraryId** (string) - The ID of the first library - **content** (object) - Contains `items`, `totalSize`, `offset`, `limit` #### Response Example { "libraries": [{ "id": "1", "title": "Movies", "type": "movie" }], "selectedLibraryId": "1", "content": { "items": [], "totalSize": 0, "offset": 0, "limit": 50 } } ### Error Responses - **400** - `limit` is not an integer, or `sort` or `sortOrder` is outside its allowed values - **500** - The page read failed, or the Maintainerr state lookup failed - **503** - The library list is unavailable because the server could not be reached ### Notes There is no way to choose which library is bootstrapped. It is always the first one the media server returned. The embedded page is always filtered to that library's own type, so a show library returns shows and never seasons or episodes. A `sort` of `manual` or `excluded` pays the full-library sweep described above, at page load. ``` -------------------------------- ### Create production build with npm run build Source: https://docs.maintainerr.info/contributing Builds the documentation site for production. Run this before opening a pull request when making structural or formatting changes. ```bash npm run build ``` -------------------------------- ### Serve production build locally with npm run serve Source: https://docs.maintainerr.info/contributing Serves the production build locally to preview the site. ```bash npm run serve ``` -------------------------------- ### GET /api/streamystats/items/{itemId} response Source: https://docs.maintainerr.info/api/streamystats Example JSON response from the /api/streamystats/items/{itemId} endpoint. episodeStats is only present for series, and firstWatched and lastWatched are null when nothing has been watched. ```json { "item": { "id": "abc123", "name": "Example title", "type": "Series" }, "totalViews": 12, "totalWatchTime": 34567, "completionRate": 0.87, "firstWatched": "2026-01-02T10:00:00.000Z", "lastWatched": "2026-03-04T21:15:00.000Z", "usersWatched": [ { "user": { "id": "u1", "name": "example-user" }, "watchCount": 3, "totalWatchTime": 8400, "completionRate": 1, "firstWatched": "2026-01-02T10:00:00.000Z", "lastWatched": "2026-02-01T20:00:00.000Z" } ], "watchHistory": [ { "user": { "id": "u1", "name": "example-user" }, "watchDate": "2026-02-01T20:00:00.000Z", "watchDuration": 2800, "completionPercentage": 98, "playMethod": "DirectPlay", "deviceName": "Living room", "clientName": "Jellyfin Web" } ], "watchCountByMonth": [ { "month": 2, "year": 2026, "watchCount": 4, "uniqueUsers": 2, "totalWatchTime": 11200 } ], "episodeStats": { "totalSeasons": 3, "totalEpisodes": 30, "watchedEpisodes": 21, "watchedSeasons": 2 } } ``` -------------------------------- ### POST /api/settings/emby Source: https://docs.maintainerr.info/api/settings Test, then store Emby credentials and make Emby the active media server. ```APIDOC ## POST /api/settings/emby ### Description Test, then store Emby credentials and make Emby the active media server. This changes the active media server without clearing anything. ### Method POST ### Endpoint /api/settings/emby ### Parameters #### Request Body - `emby_url` (string) - Required - Emby server URL - `emby_api_key` (string) - Required - Emby API key - `emby_user_id` (string) - Optional - Emby user ID ### Response #### Success Response (201) - Saved. On failure, returns `status: "NOK"`. #### Error Response (400) - Validation failed ### Response Example ```json { "status": "OK" } ``` ``` -------------------------------- ### GET /api/notifications/types - Response Source: https://docs.maintainerr.info/api/notifications Example response from the types endpoint. Lists subscribable notification event types with their numeric values. The route always returns 200 and cannot fail. ```json [ { "title": "Media Added To Collection", "id": 2 }, { "title": "Media Removed From Collection", "id": 4 }, { "title": "Media About To Be Handled", "id": 8 }, { "title": "Media Handled", "id": 16 }, { "title": "Rule Handling Failed", "id": 32 }, { "title": "Collection Handling Failed", "id": 64 }, { "title": "Overlay Applied", "id": 256 }, { "title": "Overlay Reverted", "id": 512 }, { "title": "Update Available", "id": 1024 } ] ``` -------------------------------- ### GET /api/overlays/status response Source: https://docs.maintainerr.info/api/overlays Response body for the status endpoint. The status field is 'idle', 'running', or 'error'. Poll this endpoint until status is no longer 'running' after starting a fire-and-forget run. ```json { "status": "idle", "lastRun": "2026-06-05T04:00:00.000Z", "lastResult": { "processed": 12, "reverted": 0, "skipped": 3, "errors": 0 } } ``` -------------------------------- ### GET /api/settings/media-server/switch/preview/{targetServerType} Source: https://docs.maintainerr.info/api/settings Previews what a media server switch would clear, keep, and migrate, without making any changes. It accepts a target server type and returns counts and migration details. ```APIDOC ## GET /api/settings/media-server/switch/preview/{targetServerType} ### Description Reports what a switch to the specified media server type would clear, keep, and migrate. Nothing is written and no media server is contacted. ### Method GET ### Endpoint /api/settings/media-server/switch/preview/{targetServerType} ### Parameters #### Path Parameters - **targetServerType** (string) - Required - Must be one of `plex`, `jellyfin`, or `emby`. ### Response #### Success Response (200) - `currentServerType` (string) - The current media server type. - `targetServerType` (string) - The requested target type. - `dataToBeCleared` (object) - Contains counts of data that would be cleared. `collections` here counts collections as cleared even if migration is intended, in which case they are reset rather than deleted. - `dataToBeKept` (object) - Data that would be kept. - `ruleMigration` (object) - Contains counts and a per-rule list of what could not carry over. Omitted on a fresh install with no current server. #### Response Example ```json { "currentServerType": "plex", "targetServerType": "jellyfin", "dataToBeCleared": { "collections": 5, "exclusions": 0, "memberships": 10 }, "dataToBeKept": {}, "ruleMigration": { "counts": { "migratable": 3, "nonMigratable": 1 }, "nonMigratableRules": [ "rule1" ] } } ``` ``` -------------------------------- ### POST /api/settings/download-client Request Body Source: https://docs.maintainerr.info/api/settings Example JSON body for storing download client settings. Requires download_client_type, download_client_delete_data, and download_client_fallback_ratio; partial updates are not possible. The connection is stored unverified and credentials are not trimmed. ```json { "download_client_type": "qbittorrent", "download_client_url": "http://qbittorrent:8080", "download_client_username": "user", "download_client_password": "...", "download_client_delete_data": true, "download_client_fallback_ratio": 0.5 } ``` -------------------------------- ### POST /api/settings/tracearr/servers Source: https://docs.maintainerr.info/api/settings List the Tracearr servers that match your media server, for the settings picker. Returns an array of servers with id and name. ```APIDOC ## [POST] /api/settings/tracearr/servers ### Description List the Tracearr servers that match your media server, for the settings picker. ### Method POST ### Endpoint /api/settings/tracearr/servers ### Parameters #### Request Body - **url** (string) - Required - The Tracearr URL. - **api_key** (string) - Required - The Tracearr API key. ### Response #### Success Response (201) - **servers** (array) - An array of servers with `id` and `name`. May legitimately be empty. #### Error Response (400) Validation failed. #### Error Response (502) The document fetch threw, or reading item metadata from your media server failed. ### Notes An empty array is ambiguous: it can mean the document had no server list, or the media server type filter removed every candidate. If the library list cannot be read, every candidate is returned with a warning rather than an error. ``` -------------------------------- ### POST /api/settings/test/download-client Source: https://docs.maintainerr.info/api/settings Probe a download client URL and credentials without saving. It logs in to the named instance, creating a session there. ```APIDOC ## POST /api/settings/test/download-client ### Description Probe a download client URL and credentials without saving. The full settings schema is required even for a connection test. ### Method POST ### Endpoint /api/settings/test/download-client ### Parameters #### Request Body - **download_client_type** (string) - Required - The client type to test. - **download_client_url** (string) - Required - The client URL. - **download_client_username** (string) - Required - The username. - **download_client_password** (string) - Required - The password. - **download_client_delete_data** (boolean) - Required - Must be included even for a test. - **download_client_fallback_ratio** (number) - Required - Must be included even for a test. ### Request Example { "download_client_type": "qbittorrent", "download_client_url": "http://qbittorrent:8080", "download_client_username": "user", "download_client_password": "...", "download_client_delete_data": true, "download_client_fallback_ratio": 0.5 } ### Response #### Success Response (201) - **status** (string) - `"OK"` when connected; `message` is the selected client's version. - **status** (string) - `"NOK"` with an error message on failure. #### Response Example { "status": "OK", "message": "5.0.0" } #### Error Response (400) Returned when validation fails (e.g., omitting required fields). ``` -------------------------------- ### Server-Sent Events log stream message format Source: https://docs.maintainerr.info/api/logs Example of a log event message sent over the SSE stream from GET /api/logs/stream. Each message is a 'log' event with JSON data containing message, date, and level. ```text event: log data: {"message":"Collection handler finished","date":"2026-06-05T12:00:00.000Z","level":"INFO"} ``` -------------------------------- ### Response JSON for GET /api/tasks/{id}/status Source: https://docs.maintainerr.info/api/app-and-health Example response from the endpoint. The 'time' field is the server clock at the moment of the call, 'running' indicates whether the task is currently running, and 'runningSince' is null while the task is idle. ```json { "time": "2026-06-05T12:00:00.000Z", "running": false, "runningSince": null } ``` -------------------------------- ### POST /api/settings/jellyfin/test Source: https://docs.maintainerr.info/api/settings Probe a Jellyfin server with supplied credentials. Nothing is stored. ```APIDOC ## POST /api/settings/jellyfin/test ### Description Probe a Jellyfin server with supplied credentials. Takes the same body as the save route. Nothing is stored. ### Method POST ### Endpoint /api/settings/jellyfin/test ### Parameters #### Request Body - Same body as the save route (not detailed in the source) ### Response #### Success Response (201) - `status` (string) - `"OK"` indicates connected. `message` names the server, and `users` lists administrators only. - `status` (string) - `"NOK"` indicates any failure. `message` explains why. #### Error Response (400) - Validation failed ### Response Example ```json { "status": "OK", "message": "Server name", "users": ["admin1", "admin2"] } ``` ``` -------------------------------- ### GET /api/collections/activate/{id} Source: https://docs.maintainerr.info/api/collections Marks a collection and its rule group active again. Note this is a GET that changes state. ```APIDOC ## GET /api/collections/activate/{id} ### Description Marks a collection and its rule group active again. Note this is a GET that changes state. ### Method GET ### Endpoint /api/collections/activate/{id} ### Parameters #### Path Parameters - **id** (integer) - Required - Maintainerr collection id ### Response #### Success Response (200) - Always returned, whether or not anything changed. The body is empty. #### Response Example ```json {} ``` ### Error Handling - **400** - `id` is not an integer. ### Notes - Activating does not restore membership. A previous deactivate deleted every membership row, so the collection comes back empty and stays empty until the next rule run repopulates it. - The response can never tell you whether it worked. An unknown id changes nothing and still answers `200`. ``` -------------------------------- ### GET /api/rules/count Source: https://docs.maintainerr.info/api/rules Returns the total number of rule groups as a plain text number. This counts every row with no filtering, so it will not agree with the length of GET /api/rules. ```APIDOC ## GET /api/rules/count ### Description Return the total number of rule groups. ### Method GET ### Endpoint /api/rules/count ### Response #### Success Response (200) - **count** (integer) - The count, sent as plain text. #### Response Example ``` 42 ``` #### Error Responses - **500** - The database read failed. ```