openapi: '3.0.2' info: title: 'Agregarr API' version: '1.0.0' description: | This is the documentation for the Agregarr API backend. Two primary authentication methods are supported: - **Cookie Authentication**: A valid sign-in to the `/auth/plex` or `/auth/local` will generate a valid authentication cookie. - **API Key Authentication**: Sign-in is also possible by passing an `X-Api-Key` header along with a valid API Key generated by Agregarr. tags: - name: public description: Public API endpoints requiring no authentication. - name: settings description: Endpoints related to Agregarr's settings and configuration. - name: auth description: Endpoints related to logging in or out, and the currently authenticated user. - name: users description: Endpoints related to user management. - name: search description: Endpoints related to search and discovery. - name: request description: Endpoints related to request management. - name: movies description: Endpoints related to retrieving movies and their details. - name: tv description: Endpoints related to retrieving TV series and their details. - name: other description: Endpoints related to other TMDB data - name: person description: Endpoints related to retrieving person details. - name: media description: Endpoints related to media management. - name: collection description: Endpoints related to retrieving collection details. - name: service description: Endpoints related to getting service (Radarr/Sonarr) details. - name: missing-items description: Endpoints related to missing item request tracking and management. - name: dashboard description: Endpoints related to dashboard statistics and analytics. - name: posters description: Endpoints related to poster template management, saved posters, and icon assets. - name: source-colors description: Endpoints related to managing global source color schemes for poster generation. servers: - url: '{server}/api/v1' variables: server: default: http://localhost:7171 components: schemas: User: type: object properties: id: type: integer example: 1 readOnly: true email: type: string example: 'hey@itsme.com' readOnly: true username: type: string plexToken: type: string readOnly: true plexUsername: type: string readOnly: true userType: type: integer example: 1 readOnly: true permissions: type: number example: 0 avatar: type: string readOnly: true createdAt: type: string example: '2020-09-02T05:02:23.000Z' readOnly: true updatedAt: type: string example: '2020-09-02T05:02:23.000Z' readOnly: true requestCount: type: number example: 5 readOnly: true required: - id - email - createdAt - updatedAt UserSettings: type: object properties: locale: type: string region: type: string originalLanguage: type: string MainSettings: type: object properties: apiKey: type: string readOnly: true appLanguage: type: string example: en applicationTitle: type: string example: Agregarr applicationUrl: type: string example: https://os.example.com trustProxy: type: boolean example: true csrfProtection: type: boolean example: false hideAvailable: type: boolean example: false partialRequestsEnabled: type: boolean example: false localLogin: type: boolean example: true newPlexLogin: type: boolean example: true PlexLibrary: type: object properties: id: type: string name: type: string example: Movies enabled: type: boolean example: false required: - id - name - enabled PlexSettings: type: object properties: name: type: string example: 'Main Server' readOnly: true machineId: type: string example: '1234123412341234' readOnly: true ip: type: string example: '127.0.0.1' port: type: number example: 32400 useSsl: type: boolean nullable: true libraries: type: array readOnly: true items: $ref: '#/components/schemas/PlexLibrary' webAppUrl: type: string nullable: true example: 'https://app.plex.tv/desktop' required: - name - machineId - ip - port CollectionConfig: type: object properties: id: type: string description: "Unique collection identifier (sequential number starting from 10000)" example: "10000" name: type: string example: "User Requests" type: type: string enum: ['overseerr', 'tautulli', 'trakt', 'tmdb', 'imdb', 'letterboxd', 'networks', 'multi-source'] example: 'overseerr' subtype: type: string example: 'users' template: type: string example: "{nickname}'s Requests" customMovieTemplate: type: string description: "Custom template for movie collections when mediaType is 'both'" example: "{nickname}'s Movie Requests" nullable: true customTVTemplate: type: string description: "Custom template for TV collections when mediaType is 'both'" example: "{nickname}'s TV Requests" nullable: true customPoster: oneOf: - type: string description: "Filename of custom poster image (legacy format)" example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" - type: object description: "Per-library poster mapping (libraryId -> filename)" additionalProperties: type: string example: lib1: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" lib2: "a12bc34d-58cc-4372-a567-0e02b2c3d480.jpg" nullable: true autoPoster: type: boolean description: "Auto-generate poster during sync" example: true nullable: true autoPosterTemplate: type: integer description: "Template ID for auto-generated posters (null for default template)" example: 1 nullable: true visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended maxItems: type: integer example: 20 mediaType: type: string enum: ['movie', 'tv', 'both'] example: 'both' libraryIds: type: array items: type: string description: "Array of selected library IDs (['all'] for all libraries)" example: ["1", "2"] nullable: true libraryName: type: string description: "Selected library name for display" example: "Movies" nullable: true sortOrderHome: type: integer description: "Order for Plex home screen (creation time based)" example: 0 nullable: true sortOrderLibrary: type: integer description: "Position in library (0 for A-Z section, 1+ for promoted section)" example: 0 nullable: true isLibraryPromoted: type: boolean description: "true = promoted section (uses exclamation marks), false = A-Z section" example: true parentConfigId: type: integer description: "Reference to original config when expanded from 'all' libraries" example: 1 nullable: true isExpandedConfig: type: boolean description: "True if this config was auto-generated from a parent 'all' config" example: false nullable: true customDays: type: integer description: "Number of days for Tautulli collections (required for Tautulli type)" example: 30 nullable: true minimumPlays: type: integer description: "Minimum play count for Tautulli collections (defaults to 3 if not set, 1-100)" example: 3 nullable: true tautulliStatType: type: string enum: ['plays', 'duration'] description: "Tautulli statistic type: plays (play count) or duration (watch time)" example: 'plays' nullable: true searchMissingMovies: type: boolean description: "Auto-request missing movies" example: false nullable: true searchMissingTV: type: boolean description: "Auto-request missing TV shows" example: false nullable: true autoApproveMovies: type: boolean description: "Auto-approve movie requests" example: false nullable: true autoApproveTV: type: boolean description: "Auto-approve TV show requests" example: false nullable: true maxSeasonsToRequest: type: integer description: "Max seasons for auto-approval (TV shows with more seasons require manual approval)" example: 5 nullable: true seasonsPerShowLimit: type: integer description: "Limit each TV show to only the first X seasons (0 = all seasons)" example: 2 nullable: true traktCustomListUrl: type: string description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name or https://trakt.tv/lists/official/collection-name)" example: "https://trakt.tv/users/username/lists/my-list" nullable: true tmdbCustomListUrl: type: string description: "Custom TMDb list/collection URL (e.g., https://www.themoviedb.org/list/123456)" example: "https://www.themoviedb.org/collection/123456" nullable: true imdbCustomListUrl: type: string description: "Custom IMDb list URL (e.g., https://www.imdb.com/list/ls123456789/)" example: "https://www.imdb.com/list/ls123456789/" nullable: true reverseOrder: type: boolean description: "Reverse the order of items from the source" example: false nullable: true randomizeOrder: type: boolean description: "Randomize the order of items (mutually exclusive with reverseOrder)" example: false nullable: true timeRestriction: type: object description: "Time restriction settings for the collection" properties: alwaysActive: type: boolean description: "If true, collection is always active (default)" example: true removeFromPlexWhenInactive: type: boolean description: "If true, completely remove from Plex when inactive (old behavior)" example: false nullable: true inactiveVisibilityConfig: type: object description: "Visibility settings to use when collection is inactive (only used if removeFromPlexWhenInactive is false)" properties: usersHome: type: boolean description: "Show on shared users' home screens when inactive" example: false serverOwnerHome: type: boolean description: "Show on server owner's home screen when inactive" example: false libraryRecommended: type: boolean description: "Show in library recommended section when inactive" example: true required: - usersHome - serverOwnerHome - libraryRecommended nullable: true dateRanges: type: array description: "Date ranges when collection should be active (repeated annually)" items: type: object properties: startDate: type: string description: "DD-MM format (e.g., '05-12' for 5th December)" example: "05-12" endDate: type: string description: "DD-MM format (e.g., '26-12' for 26th December)" example: "26-12" required: - startDate - endDate nullable: true weeklySchedule: type: object description: "Days of the week when collection should be active" properties: monday: type: boolean example: true tuesday: type: boolean example: true wednesday: type: boolean example: true thursday: type: boolean example: true friday: type: boolean example: true saturday: type: boolean example: false sunday: type: boolean example: false nullable: true nullable: true isActive: type: boolean description: "Whether collection is currently active (time restrictions met)" example: true readOnly: true collectionType: type: string enum: ['default_plex_hub', 'agregarr_created', 'pre_existing'] description: "Backend categorization system for different collection types" example: 'agregarr_created' nullable: true isLinked: type: boolean description: "Whether collection is actively linked to other collections" example: true nullable: true isUnlinked: type: boolean description: "True if this collection was deliberately unlinked and should not be grouped with siblings" example: false nullable: true missing: type: boolean description: "True if collection no longer exists in Plex" example: false lastSyncedAt: type: string format: date-time description: "ISO timestamp of last successful sync to Plex" example: "2024-01-15T10:30:00.000Z" nullable: true lastModifiedAt: type: string format: date-time description: "ISO timestamp when config was last modified" example: "2024-01-15T09:15:00.000Z" nullable: true needsSync: type: boolean description: "True if collection has been modified and needs to be synced to Plex" example: false nullable: true required: - id - name - type - subtype - template - visibilityConfig - maxItems CollectionConfigCreate: type: object description: "Schema for creating a new collection configuration" properties: id: type: string description: "Empty string for new collections (will be assigned sequential number by backend)" example: "" name: type: string example: "User Requests" type: type: string enum: ['overseerr', 'tautulli', 'trakt', 'tmdb', 'imdb', 'letterboxd', 'networks', 'multi-source'] example: 'overseerr' subtype: type: string example: 'users' template: type: string example: "{nickname}'s Requests" customMovieTemplate: type: string description: "Custom template for movie collections when mediaType is 'both'" example: "{nickname}'s Movie Requests" nullable: true customTVTemplate: type: string description: "Custom template for TV collections when mediaType is 'both'" example: "{nickname}'s TV Requests" nullable: true customPoster: oneOf: - type: string description: "Filename of custom poster image (legacy format)" example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" - type: object description: "Per-library poster mapping (libraryId -> filename)" additionalProperties: type: string example: lib1: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" lib2: "a12bc34d-58cc-4372-a567-0e02b2c3d480.jpg" nullable: true autoPoster: type: boolean description: "Auto-generate poster during sync" example: true nullable: true autoPosterTemplate: type: integer description: "Template ID for auto-generated posters (null for default template)" example: 1 nullable: true visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended maxItems: type: integer example: 20 mediaType: type: string enum: ['movie', 'tv', 'both'] example: 'both' libraryId: type: string description: "Selected library ID - each config is for exactly one library (optional when mediaType is 'both')" example: "1" libraryName: type: string description: "Selected library name for display (optional when mediaType is 'both')" example: "Movies" libraryIds: type: array items: type: string description: "Array of library IDs when mediaType is 'both' (backend expands into individual configs)" example: ["1", "2"] libraryNames: type: array items: type: string description: "Array of library names when mediaType is 'both' (backend expands into individual configs)" example: ["Movies", "TV Shows"] sortOrderHome: type: integer description: "Order for Plex home screen (creation time based)" example: 0 nullable: true sortOrderLibrary: type: integer description: "Order for Plex library tab (sortTitle based)" example: 0 nullable: true customDays: type: integer description: "Number of days for Tautulli collections (required for Tautulli type)" example: 30 nullable: true minimumPlays: type: integer description: "Minimum play count for Tautulli collections (defaults to 3 if not set, 1-100)" example: 3 nullable: true tautulliStatType: type: string enum: ['plays', 'duration'] description: "Tautulli statistic type: plays (play count) or duration (watch time)" example: 'plays' nullable: true searchMissingMovies: type: boolean description: "Auto-request missing movies" example: false nullable: true searchMissingTV: type: boolean description: "Auto-request missing TV shows" example: false nullable: true autoApproveMovies: type: boolean description: "Auto-approve movie requests" example: false nullable: true autoApproveTV: type: boolean description: "Auto-approve TV show requests" example: false nullable: true maxSeasonsToRequest: type: integer description: "Max seasons for auto-approval (TV shows with more seasons require manual approval)" example: 5 nullable: true seasonsPerShowLimit: type: integer description: "Limit each TV show to only the first X seasons (0 = all seasons)" example: 2 nullable: true traktCustomListUrl: type: string description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name or https://trakt.tv/lists/official/collection-name)" example: "https://trakt.tv/users/username/lists/my-list" nullable: true tmdbCustomListUrl: type: string description: "Custom TMDb list/collection URL (e.g., https://www.themoviedb.org/list/123456)" example: "https://www.themoviedb.org/collection/123456" nullable: true imdbCustomListUrl: type: string description: "Custom IMDb list URL (e.g., https://www.imdb.com/list/ls123456789/)" example: "https://www.imdb.com/list/ls123456789/" nullable: true reverseOrder: type: boolean description: "Reverse the order of items from the source" example: false nullable: true randomizeOrder: type: boolean description: "Randomize the order of items (mutually exclusive with reverseOrder)" example: false nullable: true timeRestriction: type: object description: "Time restriction settings for the collection" properties: alwaysActive: type: boolean description: "If true, collection is always active (default)" example: true removeFromPlexWhenInactive: type: boolean description: "If true, completely remove from Plex when inactive (old behavior)" example: false nullable: true inactiveVisibilityConfig: type: object description: "Visibility settings to use when collection is inactive (only used if removeFromPlexWhenInactive is false)" properties: usersHome: type: boolean description: "Show on shared users' home screens when inactive" example: false serverOwnerHome: type: boolean description: "Show on server owner's home screen when inactive" example: false libraryRecommended: type: boolean description: "Show in library recommended section when inactive" example: true required: - usersHome - serverOwnerHome - libraryRecommended nullable: true dateRanges: type: array description: "Date ranges when collection should be active (repeated annually)" items: type: object properties: startDate: type: string description: "DD-MM format (e.g., '05-12' for 5th December)" example: "05-12" endDate: type: string description: "DD-MM format (e.g., '26-12' for 26th December)" example: "26-12" required: - startDate - endDate nullable: true weeklySchedule: type: object description: "Days of the week when collection should be active" properties: monday: type: boolean example: true tuesday: type: boolean example: true wednesday: type: boolean example: true thursday: type: boolean example: true friday: type: boolean example: true saturday: type: boolean example: false sunday: type: boolean example: false nullable: true nullable: true # Multi-source collection properties isMultiSource: type: boolean description: "Enable multi-source mode for combining multiple collection sources" example: false nullable: true sources: type: array description: "Array of source configurations for multi-source collections" items: $ref: '#/components/schemas/CollectionSourceConfig' nullable: true combineMode: type: string enum: ['interleaved', 'list_order', 'randomised', 'cycle_lists'] description: "How to combine multiple sources: interleaved (alternate items), list_order (all from first, then second), randomised (shuffle all), cycle_lists (rotate active source)" example: 'list_order' nullable: true customSyncSchedule: type: object description: "Custom sync schedule configuration" properties: enabled: type: boolean description: "Enable custom sync schedule" example: false intervalHours: type: number description: "Sync interval in hours (supports decimals)" example: 24.0 minimum: 0.1 maximum: 168 nullable: true required: - id - name - visibilityConfig - maxItems CollectionConfigUpdate: type: object description: "Schema for updating collection configuration settings (excludes computed fields)" properties: id: type: string description: "Unique collection identifier (sequential number starting from 10000)" example: "10000" name: type: string example: "User Requests" type: type: string enum: ['overseerr', 'tautulli', 'trakt', 'tmdb', 'imdb', 'letterboxd', 'networks', 'multi-source'] example: 'overseerr' subtype: type: string example: 'users' template: type: string example: "{nickname}'s Requests" customMovieTemplate: type: string description: "Custom template for movie collections when mediaType is 'both'" example: "{nickname}'s Movie Requests" nullable: true customTVTemplate: type: string description: "Custom template for TV collections when mediaType is 'both'" example: "{nickname}'s TV Requests" nullable: true customPoster: oneOf: - type: string description: "Filename of custom poster image (legacy format)" example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" - type: object description: "Per-library poster mapping (libraryId -> filename)" additionalProperties: type: string example: lib1: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" lib2: "a12bc34d-58cc-4372-a567-0e02b2c3d480.jpg" nullable: true autoPoster: type: boolean description: "Auto-generate poster during sync" example: true nullable: true autoPosterTemplate: type: integer description: "Template ID for auto-generated posters (null for default template)" example: 1 nullable: true visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended maxItems: type: integer example: 20 mediaType: type: string enum: ['movie', 'tv', 'both'] example: 'both' libraryIds: type: array items: type: string description: "Array of selected library IDs (['all'] for all libraries)" example: ["1", "2"] nullable: true libraryName: type: string description: "Selected library name for display" example: "Movies" nullable: true sortOrderHome: type: integer description: "Order for Plex home screen (creation time based)" example: 0 nullable: true sortOrderLibrary: type: integer description: "Position in library (0 for A-Z section, 1+ for promoted section)" example: 0 nullable: true isLibraryPromoted: type: boolean description: "true = promoted section (uses exclamation marks), false = A-Z section" example: true parentConfigId: type: integer description: "Reference to original config when expanded from 'all' libraries" example: 1 nullable: true isExpandedConfig: type: boolean description: "True if this config was auto-generated from a parent 'all' config" example: false nullable: true customDays: type: integer description: "Number of days for Tautulli collections (required for Tautulli type)" example: 30 nullable: true minimumPlays: type: integer description: "Minimum play count for Tautulli collections (defaults to 3 if not set, 1-100)" example: 3 nullable: true tautulliStatType: type: string enum: ['plays', 'duration'] example: 'plays' nullable: true libraryNames: type: array items: type: string description: "Array of selected library names for display" example: ["Movies", "TV Shows"] nullable: true searchMissingMovies: type: boolean description: "Automatically search for missing movies" example: false nullable: true searchMissingTV: type: boolean description: "Automatically search for missing TV shows" example: false nullable: true autoApproveMovies: type: boolean description: "Auto-approve movie requests" example: false nullable: true autoApproveTV: type: boolean description: "Auto-approve TV show requests" example: false nullable: true maxSeasonsToRequest: type: integer description: "Max seasons for auto-approval (TV shows with more seasons require manual approval)" example: 5 nullable: true seasonsPerShowLimit: type: integer description: "Limit each TV show to only the first X seasons (0 = all seasons)" example: 2 nullable: true traktCustomListUrl: type: string description: "Custom Trakt list URL (e.g., https://trakt.tv/users/username/lists/list-name or https://trakt.tv/lists/official/collection-name)" example: "https://trakt.tv/users/username/lists/my-list" nullable: true tmdbCustomListUrl: type: string description: "Custom TMDb list/collection URL (e.g., https://www.themoviedb.org/list/123456)" example: "https://www.themoviedb.org/collection/123456" nullable: true imdbCustomListUrl: type: string description: "Custom IMDb list URL (e.g., https://www.imdb.com/list/ls123456789/)" example: "https://www.imdb.com/list/ls123456789/" nullable: true letterboxdCustomListUrl: type: string description: "Custom Letterboxd list URL (e.g., https://letterboxd.com/user/list/list-name/)" example: "https://letterboxd.com/user/list/my-list/" nullable: true networksCountry: type: string description: "Selected country for Networks collections (e.g., 'united-states', 'world')" example: "united-states" nullable: true reverseOrder: type: boolean description: "Reverse the order of items from the source" example: false nullable: true randomizeOrder: type: boolean description: "Randomize the order of items (mutually exclusive with reverseOrder)" example: false nullable: true timeRestriction: type: object description: "Time restriction settings for the collection" properties: alwaysActive: type: boolean description: "If true, collection is always active (default)" example: true removeFromPlexWhenInactive: type: boolean description: "If true, remove collection from Plex when inactive" example: false timeSlots: type: array description: "Array of time slot objects" items: type: object properties: startTime: type: string description: "HH:MM format" example: "09:00" endTime: type: string description: "HH:MM format" example: "17:00" nullable: true dateRanges: type: array description: "Array of date range objects" items: type: object properties: startDate: type: string description: "DD-MM format" example: "05-12" endDate: type: string description: "DD-MM format" example: "26-12" nullable: true weeklySchedule: type: object description: "Days of the week when collection should be active" properties: monday: type: boolean example: true tuesday: type: boolean example: true wednesday: type: boolean example: true thursday: type: boolean example: true friday: type: boolean example: true saturday: type: boolean example: false sunday: type: boolean example: false nullable: true nullable: true collectionType: type: string enum: ['default_plex_hub', 'agregarr_created', 'pre_existing'] description: "Backend categorization system for different collection types" example: 'agregarr_created' nullable: true isLinked: type: boolean description: "Whether collection is actively linked to other collections" example: true nullable: true isUnlinked: type: boolean description: "True if this collection was deliberately unlinked and should not be grouped with siblings" example: false nullable: true missing: type: boolean description: "True if collection no longer exists in Plex" example: false # Multi-source collection properties isMultiSource: type: boolean description: "Enable multi-source mode for combining multiple collection sources" example: false nullable: true sources: type: array description: "Array of source configurations for multi-source collections" items: $ref: '#/components/schemas/CollectionSourceConfig' nullable: true combineMode: type: string enum: ['interleaved', 'list_order', 'randomised', 'cycle_lists'] description: "How to combine multiple sources: interleaved (alternate items), list_order (all from first, then second), randomised (shuffle all), cycle_lists (rotate active source)" example: 'list_order' nullable: true customSyncSchedule: type: object description: "Custom sync schedule configuration" properties: enabled: type: boolean description: "Enable custom sync schedule" example: false intervalHours: type: number description: "Sync interval in hours (supports decimals)" example: 24.0 minimum: 0.1 maximum: 168 nullable: true required: - id - name - type - subtype - template - visibilityConfig - maxItems CollectionSourceConfig: type: object description: "Individual source configuration for multi-source collections" properties: id: type: string description: "Unique identifier for this source within the collection" example: "source-1234567890" type: type: string enum: ['trakt', 'tmdb', 'imdb', 'letterboxd', 'tautulli', 'overseerr', 'networks'] description: "Type of collection source" example: 'trakt' subtype: type: string description: "Sub-type of the collection source" example: 'trending' nullable: true customUrl: type: string description: "Custom URL for list-based sources (Trakt, TMDb, IMDb, Letterboxd)" example: "https://trakt.tv/users/username/lists/my-list" nullable: true timePeriod: type: string enum: ['daily', 'weekly', 'monthly', 'all'] description: "Time period for time-based sources" example: 'weekly' nullable: true priority: type: integer description: "Order priority when combining (0 = highest priority)" example: 0 minimum: 0 isExpanded: type: boolean description: "UI state for expandable sections" example: true nullable: true customDays: type: integer description: "Number of days to look back for Tautulli statistics" example: 30 minimum: 1 maximum: 365 nullable: true minimumPlays: type: integer description: "Minimum play count required for Tautulli sources" example: 3 minimum: 1 maximum: 100 nullable: true networksCountry: type: string description: "Selected country for Networks collections" example: "united-states" nullable: true required: - id - type - priority PlexHubConfig: type: object properties: id: type: string description: "Unique hub identifier (libraryId-hubIdentifier)" example: "1-movie.recentlyadded" hubIdentifier: type: string description: "Plex built-in hub identifier" example: "movie.recentlyadded" name: type: string description: "Display name for the hub" example: "Recently Added Movies" libraryId: type: string description: "Plex library ID" example: "1" libraryName: type: string description: "Plex library display name" example: "Films" mediaType: type: string enum: ['movie', 'tv', 'both'] description: "Media type of the hub" example: 'movie' sortOrderLibrary: type: integer description: "Position/order within the library" example: 0 visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended isActive: type: boolean description: "Whether hub is currently active (always true for built-in hubs, time restrictions for collections)" example: true readOnly: true isLinked: type: boolean description: "True if hub is actively linked to other hubs" example: false linkId: type: integer description: "Group ID for linked hubs (preserved even when isLinked=false)" example: 1 isUnlinked: type: boolean description: "True if this hub was deliberately unlinked and should not be grouped with siblings" example: false missing: type: boolean description: "True if hub no longer exists in Plex" example: false lastSyncedAt: type: string format: date-time description: "ISO timestamp of last successful sync to Plex" example: "2024-01-15T10:30:00.000Z" nullable: true lastModifiedAt: type: string format: date-time description: "ISO timestamp when config was last modified" example: "2024-01-15T09:15:00.000Z" nullable: true needsSync: type: boolean description: "True if hub has been modified and needs to be synced to Plex" example: false nullable: true required: - id - hubIdentifier - name - libraryId - libraryName - mediaType - sortOrderLibrary - visibilityConfig - isActive PlexHubConfigUpdate: type: object description: "Schema for updating PlexHubConfig settings (excludes computed fields)" properties: id: type: string description: "Unique hub identifier (libraryId-hubIdentifier)" example: "1-movie.recentlyadded" hubIdentifier: type: string description: "Plex built-in hub identifier" example: "movie.recentlyadded" name: type: string description: "Display name for the hub" example: "Recently Added Movies" libraryId: type: string description: "Plex library ID" example: "1" libraryName: type: string description: "Plex library display name" example: "Films" mediaType: type: string enum: ['movie', 'tv', 'both'] description: "Media type of the hub" example: 'movie' sortOrderHome: type: integer description: "Position/order on Plex home screen" example: 0 sortOrderLibrary: type: integer description: "Position/order within the library" example: 0 isLibraryPromoted: type: boolean description: "true = promoted section (uses exclamation marks), false = A-Z section" example: false visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended isLinked: type: boolean description: "True if hub is actively linked to other hubs" example: false linkId: type: integer description: "Group ID for linked hubs (preserved even when isLinked=false)" example: 1 isUnlinked: type: boolean description: "True if this hub was deliberately unlinked and should not be grouped with siblings" example: false missing: type: boolean description: "True if hub no longer exists in Plex" example: false timeRestriction: type: object description: "Time restriction settings for the hub" properties: alwaysActive: type: boolean description: "If true, hub is always active (default)" example: true removeFromPlexWhenInactive: type: boolean description: "If true, remove hub from Plex when inactive" example: false timeSlots: type: array description: "Array of time slot objects" items: type: object properties: startTime: type: string description: "HH:MM format" example: "09:00" endTime: type: string description: "HH:MM format" example: "17:00" nullable: true dateRanges: type: array description: "Array of date range objects" items: type: object properties: startDate: type: string description: "DD-MM format" example: "05-12" endDate: type: string description: "DD-MM format" example: "26-12" nullable: true weeklySchedule: type: object properties: monday: type: boolean tuesday: type: boolean wednesday: type: boolean thursday: type: boolean friday: type: boolean saturday: type: boolean sunday: type: boolean nullable: true nullable: true required: - id - hubIdentifier - name - libraryId - libraryName - mediaType - sortOrderLibrary - visibilityConfig PreExistingCollectionConfig: type: object properties: id: type: string description: "Unique collection identifier (libraryId-collectionRatingKey)" example: "1-35954" collectionRatingKey: type: string description: "Plex collection rating key" example: "35954" name: type: string description: "Display name from Plex" example: "Action Movies" libraryId: type: string description: "Plex library ID" example: "1" libraryName: type: string description: "Plex library display name" example: "Films" mediaType: type: string enum: ['movie', 'tv', 'both'] description: "Media type based on collection contents" example: 'movie' sortOrderHome: type: integer description: "Position on Plex home screen" example: 0 sortOrderLibrary: type: integer description: "Position in library (0 for A-Z section, 1+ for promoted section)" example: 0 isLibraryPromoted: type: boolean description: "true = promoted section (uses exclamation marks), false = A-Z section" example: false visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended isActive: type: boolean description: "Whether collection is currently active (computed from time restrictions)" example: true readOnly: true collectionType: type: string enum: ['default_plex_hub', 'agregarr_created', 'pre_existing'] description: "Type of collection (determined during discovery)" example: 'pre_existing' readOnly: true isLinked: type: boolean description: "True if collection is actively linked to other collections" example: false linkId: type: integer description: "Group ID for linked collections (preserved even when isLinked=false)" example: 1 isUnlinked: type: boolean description: "True if this collection was deliberately unlinked and should not be grouped with siblings" example: false nullable: true timeRestriction: type: object properties: alwaysActive: type: boolean description: "If true, collection is always active (default)" example: true removeFromPlexWhenInactive: type: boolean description: "If true, completely remove from Plex when inactive" example: false nullable: true inactiveVisibilityConfig: type: object properties: usersHome: type: boolean serverOwnerHome: type: boolean libraryRecommended: type: boolean nullable: true dateRanges: type: array items: type: object properties: startDate: type: string description: "DD-MM format (e.g., '05-12' for 5th December)" example: "05-12" endDate: type: string description: "DD-MM format (e.g., '26-12' for 26th December)" example: "26-12" required: - startDate - endDate nullable: true weeklySchedule: type: object properties: monday: type: boolean tuesday: type: boolean wednesday: type: boolean thursday: type: boolean friday: type: boolean saturday: type: boolean sunday: type: boolean nullable: true required: - alwaysActive nullable: true customPoster: type: string description: "Path to custom poster image file" example: "/app/config/poster-123.jpg" nullable: true autoPoster: type: boolean description: "Auto-generate poster during sync" example: true nullable: true missing: type: boolean description: "True if collection no longer exists in Plex" example: false lastSyncedAt: type: string format: date-time description: "ISO timestamp of last successful sync to Plex" example: "2024-01-15T10:30:00.000Z" nullable: true lastModifiedAt: type: string format: date-time description: "ISO timestamp when config was last modified" example: "2024-01-15T09:15:00.000Z" nullable: true needsSync: type: boolean description: "True if collection has been modified and needs to be synced to Plex" example: false nullable: true required: - id - collectionRatingKey - name - libraryId - libraryName - mediaType - sortOrderHome - sortOrderLibrary - visibilityConfig - isActive PreExistingCollectionConfigUpdate: type: object description: "Schema for updating PreExistingCollectionConfig settings (excludes computed fields)" properties: id: type: string description: "Unique collection identifier (libraryId-collectionRatingKey)" example: "1-35954" collectionRatingKey: type: string description: "Plex collection rating key" example: "35954" name: type: string description: "Display name from Plex" example: "Action Movies" libraryId: type: string description: "Plex library ID" example: "1" libraryName: type: string description: "Plex library display name" example: "Films" mediaType: type: string enum: ['movie', 'tv', 'both'] description: "Media type based on collection contents" example: 'movie' sortOrderHome: type: integer description: "Position on Plex home screen" example: 0 sortOrderLibrary: type: integer description: "Position in library (0 for A-Z section, 1+ for promoted section)" example: 0 isLibraryPromoted: type: boolean description: "true = promoted section (uses exclamation marks), false = A-Z section" example: false visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended isLinked: type: boolean description: "True if collection is actively linked to other collections" example: false linkId: type: integer description: "Group ID for linked collections (preserved even when isLinked=false)" example: 1 isUnlinked: type: boolean description: "True if this collection was deliberately unlinked and should not be grouped with siblings" example: false timeRestriction: type: object description: "Time restriction settings for the collection" properties: alwaysActive: type: boolean description: "If true, collection is always active (default)" example: true removeFromPlexWhenInactive: type: boolean description: "If true, remove collection from Plex when inactive" example: false timeSlots: type: array description: "Array of time slot objects" items: type: object properties: startTime: type: string description: "HH:MM format" example: "09:00" endTime: type: string description: "HH:MM format" example: "17:00" nullable: true dateRanges: type: array description: "Array of date range objects" items: type: object properties: startDate: type: string description: "DD-MM format" example: "05-12" endDate: type: string description: "DD-MM format" example: "26-12" nullable: true weeklySchedule: type: object properties: monday: type: boolean tuesday: type: boolean wednesday: type: boolean thursday: type: boolean friday: type: boolean saturday: type: boolean sunday: type: boolean nullable: true required: - alwaysActive nullable: true customPoster: oneOf: - type: string description: "Filename of custom poster image (legacy format)" example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" - type: object description: "Per-library poster mapping (libraryId -> filename)" additionalProperties: type: string example: lib1: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" lib2: "a12bc34d-58cc-4372-a567-0e02b2c3d480.jpg" nullable: true autoPoster: type: boolean description: "Auto-generate poster during sync" example: true nullable: true missing: type: boolean description: "True if collection no longer exists in Plex" example: false required: - id - collectionRatingKey - name - libraryId - libraryName - mediaType - sortOrderHome - sortOrderLibrary - visibilityConfig DiscoveredCollectionConfig: type: object description: "Pre-existing collection config for discovery (collectionRatingKey is extracted server-side)" properties: id: type: string description: "Unique collection identifier (libraryId-collectionRatingKey)" example: "1:36466" hubIdentifier: type: string description: "Plex hub identifier for the collection" example: "custom.collection.1.36466" name: type: string description: "Display name from Plex" example: "movies that i like" libraryId: type: string description: "Plex library ID" example: "1" libraryName: type: string description: "Plex library display name" example: "Films" mediaType: type: string enum: ['movie', 'tv', 'both'] description: "Media type based on collection contents" example: 'movie' sortOrderHome: type: integer description: "Position on Plex home screen" example: 0 sortOrderLibrary: type: integer description: "Position in library" example: 0 visibilityConfig: type: object properties: usersHome: type: boolean example: false serverOwnerHome: type: boolean example: true libraryRecommended: type: boolean example: false required: - usersHome - serverOwnerHome - libraryRecommended collectionType: type: string description: "Type of collection for discovery" example: "pre_existing" required: - id - name - libraryId - libraryName - mediaType - sortOrderHome - sortOrderLibrary - visibilityConfig PlexConnection: type: object properties: protocol: type: string example: 'https' address: type: string example: '127.0.0.1' port: type: number example: 32400 uri: type: string example: 'https://127-0-0-1.2ab6ce1a093d465e910def96cf4e4799.plex.direct:32400' local: type: boolean example: true status: type: number example: 200 message: type: string example: 'OK' required: - protocol - address - port - uri - local PlexDevice: type: object properties: name: type: string example: 'My Plex Server' product: type: string example: 'Plex Media Server' productVersion: type: string example: '1.21' platform: type: string example: 'Linux' platformVersion: type: string example: 'default/linux/amd64/17.1/systemd' device: type: string example: 'PC' clientIdentifier: type: string example: '85a943ce-a0cc-4d2a-a4ec-f74f06e40feb' createdAt: type: string example: '2021-01-01T00:00:00.000Z' lastSeenAt: type: string example: '2021-01-01T00:00:00.000Z' provides: type: array items: type: string example: 'server' owned: type: boolean example: true ownerID: type: string example: '12345' home: type: boolean example: true sourceTitle: type: string example: 'xyzabc' accessToken: type: string example: 'supersecretaccesstoken' publicAddress: type: string example: '127.0.0.1' httpsRequired: type: boolean example: true synced: type: boolean example: true relay: type: boolean example: true dnsRebindingProtection: type: boolean example: false natLoopbackSupported: type: boolean example: false publicAddressMatches: type: boolean example: false presence: type: boolean example: true connection: type: array items: $ref: '#/components/schemas/PlexConnection' required: - name - product - productVersion - platform - device - clientIdentifier - createdAt - lastSeenAt - provides - owned - connection TautulliSettings: type: object properties: hostname: type: string nullable: true example: 'tautulli.example.com' port: type: number nullable: true example: 8181 useSsl: type: boolean nullable: true urlBase: type: string nullable: true example: '/tautulli' apiKey: type: string nullable: true externalUrl: type: string nullable: true TraktSettings: type: object properties: apiKey: type: string nullable: true example: 'your-trakt-api-key-here' OverseerrSettings: type: object properties: hostname: type: string nullable: true example: 'overseerr.example.com' port: type: number nullable: true example: 5055 useSsl: type: boolean nullable: true example: false urlBase: type: string nullable: true example: '/overseerr' apiKey: type: string nullable: true example: 'your-agregarr-api-key-here' externalUrl: type: string nullable: true example: 'https://overseerr.example.com:5055' ServiceUserSettings: type: object properties: userCreationMode: type: string enum: ['single', 'per-service', 'granular'] example: 'per-service' description: 'How to create service users - single (one Agregarr user), per-service (TraktAgregarr, TMDbAgregarr), or granular (TraktTrendingAgregarr, TMDbPopularAgregarr)' RadarrSettings: type: object properties: id: type: number example: 0 readOnly: true name: type: string example: 'Radarr Main' hostname: type: string example: '127.0.0.1' port: type: number example: 7878 apiKey: type: string example: 'exampleapikey' useSsl: type: boolean example: false baseUrl: type: string activeProfileId: type: number example: 1 activeProfileName: type: string example: 720p/1080p activeDirectory: type: string example: '/movies' minimumAvailability: type: string example: 'In Cinema' isDefault: type: boolean example: false externalUrl: type: string example: http://radarr.example.com syncEnabled: type: boolean example: false preventSearch: type: boolean example: false required: - hostname - port - apiKey - useSsl - activeProfileId - activeProfileName - activeDirectory - minimumAvailability - isDefault SonarrSettings: type: object properties: id: type: number example: 0 readOnly: true name: type: string example: 'Sonarr Main' hostname: type: string example: '127.0.0.1' port: type: number example: 8989 apiKey: type: string example: 'exampleapikey' useSsl: type: boolean example: false baseUrl: type: string activeProfileId: type: number example: 1 activeProfileName: type: string example: 720p/1080p activeDirectory: type: string example: '/tv/' activeLanguageProfileId: type: number example: 1 activeAnimeProfileId: type: number nullable: true activeAnimeLanguageProfileId: type: number nullable: true activeAnimeProfileName: type: string example: 720p/1080p nullable: true activeAnimeDirectory: type: string nullable: true enableSeasonFolders: type: boolean example: false isDefault: type: boolean example: false externalUrl: type: string example: http://radarr.example.com syncEnabled: type: boolean example: false preventSearch: type: boolean example: false required: - hostname - port - apiKey - useSsl - activeProfileId - activeProfileName - activeDirectory - isDefault ServarrTag: type: object properties: id: type: number example: 1 label: type: string example: A Label PublicSettings: type: object properties: initialized: type: boolean example: false MovieResult: type: object required: - id - mediaType - title properties: id: type: number example: 1234 mediaType: type: string popularity: type: number example: 10 posterPath: type: string backdropPath: type: string voteCount: type: number voteAverage: type: number genreIds: type: array items: type: number overview: type: string example: 'Overview of the movie' originalLanguage: type: string example: 'en' title: type: string example: Movie Title originalTitle: type: string example: Original Movie Title releaseDate: type: string adult: type: boolean example: false video: type: boolean example: false TvResult: type: object properties: id: type: number example: 1234 mediaType: type: string popularity: type: number example: 10 posterPath: type: string backdropPath: type: string voteCount: type: number voteAverage: type: number genreIds: type: array items: type: number overview: type: string example: 'Overview of the movie' originalLanguage: type: string example: 'en' name: type: string example: TV Show Name originalName: type: string example: Original TV Show Name originCountry: type: array items: type: string firstAirDate: type: string PersonResult: type: object properties: id: type: number example: 12345 profilePath: type: string adult: type: boolean example: false mediaType: type: string default: 'person' knownFor: type: array items: oneOf: - $ref: '#/components/schemas/MovieResult' - $ref: '#/components/schemas/TvResult' Genre: type: object properties: id: type: number example: 1 name: type: string example: Adventure Company: type: object properties: id: type: number example: 1 logo_path: type: string nullable: true name: type: string ProductionCompany: type: object properties: id: type: number example: 1 logoPath: type: string nullable: true originCountry: type: string name: type: string Network: type: object properties: id: type: number example: 1 logoPath: type: string nullable: true originCountry: type: string name: type: string RelatedVideo: type: object properties: url: type: string example: https://www.youtube.com/watch?v=9qhL2_UxXM0/ key: type: string example: 9qhL2_UxXM0 name: type: string example: Trailer for some movie (1978) size: type: number example: 1080 type: type: string example: Trailer enum: - Clip - Teaser - Trailer - Featurette - Opening Credits - Behind the Scenes - Bloopers site: type: string enum: - 'YouTube' MovieDetails: type: object properties: id: type: number example: 123 readOnly: true imdbId: type: string example: 'tt123' adult: type: boolean backdropPath: type: string posterPath: type: string budget: type: number example: 1000000 genres: type: array items: $ref: '#/components/schemas/Genre' homepage: type: string relatedVideos: type: array items: $ref: '#/components/schemas/RelatedVideo' originalLanguage: type: string originalTitle: type: string overview: type: string popularity: type: number productionCompanies: type: array items: $ref: '#/components/schemas/ProductionCompany' productionCountries: type: array items: type: object properties: iso_3166_1: type: string name: type: string releaseDate: type: string releases: type: object properties: results: type: array items: type: object properties: iso_3166_1: type: string example: 'US' rating: type: string nullable: true release_dates: type: array items: type: object properties: certification: type: string example: 'PG-13' iso_639_1: type: string nullable: true note: type: string nullable: true example: 'Blu ray' release_date: type: string example: '2017-07-12T00:00:00.000Z' type: type: number example: 1 revenue: type: number nullable: true runtime: type: number status: type: string tagline: type: string title: type: string video: type: boolean voteAverage: type: number voteCount: type: number collection: type: object properties: id: type: number example: 1 name: type: string example: A collection posterPath: type: string backdropPath: type: string externalIds: $ref: '#/components/schemas/ExternalIds' watchProviders: type: array items: $ref: '#/components/schemas/WatchProviders' Episode: type: object properties: id: type: number name: type: string airDate: type: string nullable: true episodeNumber: type: number overview: type: string productionCode: type: string seasonNumber: type: number showId: type: number stillPath: type: string nullable: true voteAverage: type: number voteCount: type: number Season: type: object properties: id: type: number airDate: type: string nullable: true episodeCount: type: number name: type: string overview: type: string posterPath: type: string seasonNumber: type: number episodes: type: array items: $ref: '#/components/schemas/Episode' TvDetails: type: object properties: id: type: number example: 123 backdropPath: type: string posterPath: type: string contentRatings: type: object properties: results: type: array items: type: object properties: iso_3166_1: type: string example: 'US' rating: type: string example: 'TV-14' createdBy: type: array items: type: object properties: id: type: number name: type: string gender: type: number profilePath: type: string nullable: true episodeRunTime: type: array items: type: number firstAirDate: type: string genres: type: array items: $ref: '#/components/schemas/Genre' homepage: type: string inProduction: type: boolean languages: type: array items: type: string lastAirDate: type: string lastEpisodeToAir: $ref: '#/components/schemas/Episode' name: type: string nextEpisodeToAir: $ref: '#/components/schemas/Episode' networks: type: array items: $ref: '#/components/schemas/ProductionCompany' numberOfEpisodes: type: number numberOfSeason: type: number originCountry: type: array items: type: string originalLanguage: type: string originalName: type: string overview: type: string popularity: type: number productionCompanies: type: array items: $ref: '#/components/schemas/ProductionCompany' productionCountries: type: array items: type: object properties: iso_3166_1: type: string name: type: string seasons: type: array items: $ref: '#/components/schemas/Season' status: type: string tagline: type: string type: type: string voteAverage: type: number voteCount: type: number externalIds: $ref: '#/components/schemas/ExternalIds' keywords: type: array items: $ref: '#/components/schemas/Keyword' watchProviders: type: array items: $ref: '#/components/schemas/WatchProviders' ExternalIds: type: object properties: facebookId: type: string nullable: true freebaseId: type: string nullable: true freebaseMid: type: string nullable: true imdbId: type: string nullable: true instagramId: type: string nullable: true tvdbId: type: number nullable: true tvrageId: type: number nullable: true twitterId: type: string nullable: true ServiceProfile: type: object properties: id: type: number example: 1 name: type: string example: 720p/1080p PageInfo: type: object properties: page: type: number example: 1 pages: type: number example: 10 results: type: number example: 100 Job: type: object properties: id: type: string example: job-name type: type: string enum: [process, command] interval: type: string enum: [short, long, fixed] name: type: string example: A Job Name nextExecutionTime: type: string example: '2020-09-02T05:02:23.000Z' running: type: boolean example: false Keyword: type: object properties: id: type: number example: 1 name: type: string example: 'anime' SonarrSeries: type: object properties: title: type: string example: COVID-25 sortTitle: type: string example: covid 25 seasonCount: type: number example: 1 status: type: string example: upcoming overview: type: string example: The thread is picked up again by Marianne Schmidt which ... network: type: string example: CBS airTime: type: string example: 02:15 images: type: array items: type: object properties: coverType: type: string example: banner url: type: string example: /sonarr/MediaCoverProxy/6467f05d9872726ad08cbf920e5fee4bf69198682260acab8eab5d3c2c958e92/5c8f116c6aa5c.jpg remotePoster: type: string example: https://artworks.thetvdb.com/banners/posters/5c8f116129983.jpg seasons: type: array items: type: object properties: seasonNumber: type: number example: 1 monitored: type: boolean example: true year: type: number example: 2015 path: type: string profileId: type: number languageProfileId: type: number seasonFolder: type: boolean monitored: type: boolean useSceneNumbering: type: boolean runtime: type: number tvdbId: type: number example: 12345 tvRageId: type: number tvMazeId: type: number firstAired: type: string lastInfoSync: type: string nullable: true seriesType: type: string cleanTitle: type: string imdbId: type: string titleSlug: type: string certification: type: string genres: type: array items: type: string tags: type: array items: type: string added: type: string ratings: type: array items: type: object properties: votes: type: number value: type: number qualityProfileId: type: number id: type: number nullable: true rootFolderPath: type: string nullable: true addOptions: type: array items: type: object properties: ignoreEpisodesWithFiles: type: boolean nullable: true ignoreEpisodesWithoutFiles: type: boolean nullable: true searchForMissingEpisodes: type: boolean nullable: true WatchProviders: type: array items: type: object properties: iso_3166_1: type: string link: type: string buy: type: array items: $ref: '#/components/schemas/WatchProviderDetails' flatrate: items: $ref: '#/components/schemas/WatchProviderDetails' WatchProviderDetails: type: object properties: displayPriority: type: number logoPath: type: string id: type: number name: type: string WatchProviderRegion: type: object properties: iso_3166_1: type: string english_name: type: string native_name: type: string MissingItemRequest: type: object properties: id: type: integer example: 1 tmdbId: type: integer example: 550 mediaType: type: string enum: [movie, tv] example: movie title: type: string example: "Fight Club" posterPath: type: string nullable: true example: "/pB8BM7pdSp6B6Ih7QZ4DrQ3PmJK.jpg" year: type: integer nullable: true example: 1999 collectionName: type: string example: "Trending Movies" collectionSource: type: string enum: [trakt, tmdb, imdb, letterboxd] example: "trakt" collectionSubtype: type: string nullable: true example: "trending" requestService: type: string enum: [overseerr, radarr, sonarr] example: "overseerr" requestMethod: type: string enum: [auto, manual] example: "auto" requestStatus: type: string enum: [pending, approved, declined, available, processing, failed, partially_available] example: "approved" overseerrRequestId: type: integer nullable: true example: 123 requestedBy: type: object nullable: true properties: id: type: integer example: 1 displayName: type: string example: "TraktAgregarr" createdAt: type: string format: date-time example: "2023-12-01T10:30:00Z" requestedAt: type: string format: date-time nullable: true example: "2023-12-01T10:35:00Z" MissingItemsResponse: type: object properties: results: type: array items: $ref: '#/components/schemas/MissingItemRequest' total: type: integer example: 150 limit: type: integer example: 20 offset: type: integer example: 0 MissingItemStats: type: object properties: stats: type: object properties: total: type: integer example: 150 pending: type: integer example: 45 approved: type: integer example: 90 declined: type: integer example: 10 available: type: integer example: 5 processing: type: integer example: 12 failed: type: integer example: 2 partially_available: type: integer example: 3 byMediaType: type: object properties: movie: type: integer example: 85 tv: type: integer example: 65 bySource: type: object properties: trakt: type: integer example: 60 tmdb: type: integer example: 45 imdb: type: integer example: 30 letterboxd: type: integer example: 15 DashboardStats: type: object properties: collections: type: object properties: configured: type: integer example: 25 active: type: integer example: 18 stats: type: object nullable: true properties: topCollections: type: array items: type: object totalCollections: type: integer example: 15 activity: type: object nullable: true properties: totalPlays: type: integer example: 156 moviePlays: type: integer example: 89 tvPlays: type: integer example: 67 tautulli: type: object nullable: true properties: isConnected: type: boolean example: true error: type: string nullable: true example: null weeklyActivity: type: object nullable: true properties: totalPlays: type: integer example: 156 moviePlays: type: integer example: 89 tvPlays: type: integer example: 67 timestamp: type: string format: date-time example: "2024-01-15T10:30:00Z" CollectionStatsResponse: type: object properties: collections: type: array items: $ref: '#/components/schemas/CollectionStats' metadata: type: object properties: limit: type: integer example: 10 statType: type: string enum: [plays, duration] example: "plays" days: type: integer example: 30 timestamp: type: string format: date-time example: "2024-01-15T10:30:00Z" CollectionStats: type: object properties: rating_key: type: string example: "12345" title: type: string example: "Marvel Movies" media_type: type: string example: "collection" section_id: type: integer example: 1 section_name: type: string example: "Movies" item_count: type: integer example: 28 total_plays: type: integer example: 145 total_duration: type: integer example: 18000 last_played: type: integer nullable: true example: 1642291200 play_count: type: integer nullable: true example: 145 watch_time_stats: type: array items: type: object properties: query_days: type: integer example: 30 total_time: type: integer example: 18000 total_plays: type: integer example: 145 user_stats: type: array items: type: object properties: friendly_name: type: string example: "John Doe" user_id: type: integer example: 1 total_plays: type: integer example: 45 total_time: type: integer example: 5400 ActivityStatsResponse: type: object properties: activity: type: object properties: topMovies: type: array items: type: object topTV: type: array items: type: object tautulliInfo: type: object nullable: true metadata: type: object properties: days: type: integer example: 7 limit: type: integer example: 10 timestamp: type: string format: date-time example: "2024-01-15T10:30:00Z" # Discovery-specific schemas that allow collectionType to be sent (not readOnly) PlexHubConfigForDiscovery: type: object description: "PlexHubConfig schema for discovery operations - allows collectionType to be sent" properties: id: type: string description: "Unique hub identifier (libraryId-hubIdentifier)" example: "1-movie.recentlyadded" hubIdentifier: type: string description: "Plex built-in hub identifier" example: "movie.recentlyadded" name: type: string description: "Display name for the hub" example: "Recently Added Movies" libraryId: type: string description: "Plex library ID" example: "1" libraryName: type: string description: "Plex library display name" example: "Films" mediaType: type: string enum: ['movie', 'tv', 'both'] description: "Media type of the hub" example: 'movie' sortOrderLibrary: type: integer description: "Position/order within the library" example: 0 visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended isActive: type: boolean description: "Whether hub is currently active" example: true readOnly: true collectionType: type: string enum: ['default_plex_hub', 'agregarr_created', 'pre_existing'] description: "Type of collection (determined during discovery)" example: 'default_plex_hub' isLinked: type: boolean description: "True if hub is actively linked to other hubs" example: false linkId: type: integer description: "Group ID for linked hubs" example: 1 isUnlinked: type: boolean description: "True if this hub was deliberately unlinked" example: false required: - id - hubIdentifier - name - libraryId - libraryName - mediaType - sortOrderLibrary - visibilityConfig - isActive PreExistingCollectionConfigForDiscovery: type: object description: "PreExistingCollectionConfig schema for discovery operations - allows collectionType to be sent" properties: id: type: string description: "Unique collection identifier (libraryId-collectionRatingKey)" example: "1-35954" collectionRatingKey: type: string description: "Plex collection rating key" example: "35954" name: type: string description: "Display name from Plex" example: "Action Movies" libraryId: type: string description: "Plex library ID" example: "1" libraryName: type: string description: "Plex library display name" example: "Films" mediaType: type: string enum: ['movie', 'tv', 'both'] description: "Media type based on collection contents" example: 'movie' sortOrderHome: type: integer description: "Position on Plex home screen" example: 0 sortOrderLibrary: type: integer description: "Position in library" example: 0 visibilityConfig: type: object properties: usersHome: type: boolean description: "Show on shared users' home screens" example: true serverOwnerHome: type: boolean description: "Show on server owner's home screen" example: false libraryRecommended: type: boolean description: "Show in library recommended section" example: false required: - usersHome - serverOwnerHome - libraryRecommended isActive: type: boolean description: "Whether collection is currently active" example: true readOnly: true collectionType: type: string enum: ['default_plex_hub', 'agregarr_created', 'pre_existing'] description: "Type of collection (determined during discovery)" example: 'pre_existing' isLinked: type: boolean description: "True if collection is actively linked to other collections" example: false linkId: type: integer description: "Group ID for linked collections" example: 1 isUnlinked: type: boolean description: "True if this collection was deliberately unlinked" example: false timeRestriction: type: object properties: alwaysActive: type: boolean example: true dateRanges: type: array items: type: object properties: startDate: type: string description: "DD-MM format" example: "05-12" endDate: type: string description: "DD-MM format" example: "26-12" nullable: true weeklySchedule: type: object properties: monday: type: boolean tuesday: type: boolean wednesday: type: boolean thursday: type: boolean friday: type: boolean saturday: type: boolean sunday: type: boolean nullable: true nullable: true customPoster: type: string description: "Custom poster filename" nullable: true autoPoster: type: boolean description: "Auto-generate poster during sync" example: true nullable: true required: - id - collectionRatingKey - name - libraryId - libraryName - mediaType - sortOrderHome - sortOrderLibrary - visibilityConfig - isActive NetworksCountryOption: type: object description: "Country option for Networks collections" properties: value: type: string description: "Country code/identifier" example: "united-states" label: type: string description: "Display name for country" example: "United States" required: - value - label NetworksPlatformOption: type: object description: "Platform option for Networks collections" properties: value: type: string description: "Platform identifier for Networks collections" example: "netflix_top_10" label: type: string description: "Display name for platform" example: "Netflix Top 10" required: - value - label responses: NotFound: description: Resource not found content: application/json: schema: type: object properties: message: type: string example: "not found" errors: type: array items: type: object properties: path: type: string example: "/api/v1/source-colors" message: type: string example: "not found" required: - path - message required: - message - errors securitySchemes: cookieAuth: type: apiKey name: agregarr.sid in: cookie apiKey: type: apiKey in: header name: X-Api-Key paths: /status: get: summary: Get Agregarr status description: Returns the current Agregarr status in a JSON object. security: [] tags: - public responses: '200': description: Returned status content: application/json: schema: type: object properties: version: type: string example: 1.0.0 commitTag: type: string updateAvailable: type: boolean commitsBehind: type: number restartRequired: type: boolean /status/appdata: get: summary: Get application data volume status description: For Docker installs, returns whether or not the volume mount was configured properly. Always returns true for non-Docker installs. security: [] tags: - public responses: '200': description: Application data volume status and path content: application/json: schema: type: object properties: appData: type: boolean example: true appDataPath: type: string example: /app/config /collections: get: summary: Get collection configurations description: Returns all collection configurations. tags: - collection responses: '200': description: Collection configurations retrieved successfully content: application/json: schema: type: object properties: collectionConfigs: type: array items: $ref: '#/components/schemas/CollectionConfig' post: summary: Save collection configurations description: Updates collection configurations with the provided values. tags: - collection requestBody: required: true content: application/json: schema: type: object properties: collectionConfigs: type: array items: $ref: '#/components/schemas/CollectionConfig' responses: '200': description: Collection configurations saved successfully content: application/json: schema: type: object properties: collectionConfigs: type: array items: $ref: '#/components/schemas/CollectionConfig' message: type: string example: "Collection configurations saved successfully" '500': description: Failed to save collection configurations /collections/create: post: summary: Create a new collection description: Creates a new collection configuration. tags: - collection requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CollectionConfigCreate' responses: '201': description: Collection created successfully content: application/json: schema: type: object properties: collectionConfig: $ref: '#/components/schemas/CollectionConfig' message: type: string example: "Collection created successfully" '500': description: Failed to create collection /collections/{id}/settings: put: summary: Update individual collection settings description: Updates settings for a specific collection while preserving computed fields. tags: - collection parameters: - name: id in: path required: true description: Collection ID schema: type: string example: "collection-123" requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CollectionConfigUpdate' responses: '200': description: Collection settings updated successfully content: application/json: schema: type: object properties: collectionConfig: $ref: '#/components/schemas/CollectionConfig' message: type: string example: "Collection settings updated successfully" '404': description: Collection not found '500': description: Failed to update collection settings /collections/{id}: delete: summary: Delete individual collection description: Delete individual collection and recalculate sort orders. If the collection is linked, all linked collections will be deleted. tags: - collection parameters: - name: id in: path required: true description: Collection ID schema: type: string example: "collection-123" responses: '200': description: Collection(s) deleted successfully content: application/json: schema: type: object properties: message: type: string example: "1 collection(s) deleted successfully" deletedConfigs: type: array items: type: object properties: id: type: string name: type: string '404': description: Collection not found content: application/json: schema: type: object properties: error: type: string example: "Collection not found" message: type: string example: "Collection with id \"collection-123\" not found" '500': description: Failed to delete collection content: application/json: schema: type: object properties: error: type: string example: "Failed to delete collection" message: type: string /collections/{id}/sync: post: summary: Sync individual collection description: Triggers sync for a specific collection by ID. This performs the same sync operation as the global sync but only for the specified collection, without the global preprocessing overhead. tags: - collection parameters: - name: id in: path required: true description: Collection ID to sync schema: type: string example: "10000" responses: '200': description: Collection sync started successfully content: application/json: schema: type: object properties: status: type: string example: "success" message: type: string example: "Collection sync started for \"Movie Collection\"" collectionId: type: string example: "10000" collectionName: type: string example: "Movie Collection" '404': description: Collection not found content: application/json: schema: type: object properties: status: type: string example: "error" message: type: string example: "Collection with ID 10000 not found" '500': description: Failed to start collection sync content: application/json: schema: type: object properties: status: type: string example: "error" message: type: string example: "Admin user not found" /collections/{id}/promote: patch: summary: Promote collection to top section description: Moves a collection from the A-Z alphabetical section to the promoted section with custom ordering. tags: - collection parameters: - name: id in: path required: true description: Collection ID schema: type: string example: "collection-123" responses: '200': description: Collection promoted successfully content: application/json: schema: type: object properties: success: type: boolean example: true config: $ref: '#/components/schemas/CollectionConfig' '400': description: Collection is already in promoted section content: application/json: schema: type: object properties: error: type: string example: "Collection is already in promoted section" '404': description: Collection not found content: application/json: schema: type: object properties: error: type: string example: "Collection not found" '500': description: Failed to promote collection content: application/json: schema: type: object properties: error: type: string example: "Failed to promote collection" /collections/{id}/demote: patch: summary: Demote collection to A-Z section description: Moves a collection from the promoted section to the A-Z alphabetical section. tags: - collection parameters: - name: id in: path required: true description: Collection ID schema: type: string example: "collection-123" responses: '200': description: Collection demoted successfully content: application/json: schema: type: object properties: success: type: boolean example: true config: $ref: '#/components/schemas/CollectionConfig' '400': description: Collection is already in A-Z section content: application/json: schema: type: object properties: error: type: string example: "Collection is already in A-Z section" '404': description: Collection not found content: application/json: schema: type: object properties: error: type: string example: "Collection not found" '500': description: Failed to demote collection content: application/json: schema: type: object properties: error: type: string example: "Failed to demote collection" /collections/sync: get: summary: Get collections sync status description: Returns the current status of collection synchronization. tags: - collection responses: '200': description: Sync status retrieved successfully content: application/json: schema: type: object properties: running: type: boolean example: false message: type: string example: "Not running" post: summary: Start collection sync description: Starts a collection synchronization process in the background. tags: - collection responses: '200': description: Collection sync started successfully content: application/json: schema: type: object properties: status: type: string example: "success" message: type: string example: "Collections sync started in background" '400': description: Bad request - missing configuration or connection issue '500': description: Failed to start collection sync /collections/sync/status: get: summary: Get detailed collections sync status description: Returns detailed sync status including current stage, progress, and global sync information. tags: - collection responses: '200': description: Detailed sync status retrieved successfully content: application/json: schema: type: object properties: running: type: boolean description: Whether a sync is currently running example: true currentStage: type: string description: Current stage of the sync process example: "Processing collections..." totalCollections: type: integer description: Total number of collections to process example: 15 processedCollections: type: integer description: Number of collections processed so far example: 8 progress: type: integer description: Sync progress percentage (0-100) example: 53 lastGlobalSyncAt: type: string format: date-time description: ISO timestamp of last successful global sync example: "2024-01-15T10:30:00.000Z" globalSyncError: type: string nullable: true description: Last global sync error message, if any example: null collectionsNeedingSync: type: integer description: Number of collections that need to be synced example: 7 /collections/preview-template: post: summary: Preview a collection template description: Generates a preview of a collection name template with given context. tags: - collection requestBody: required: true content: application/json: schema: type: object properties: template: type: string example: "{{mediaType}} {{type}} Collection" mediaType: type: string enum: [movie, tv] example: "movie" type: type: string example: "trakt" subtype: type: string example: "" customDays: type: number example: 30 minimumPlays: type: number example: 3 required: - template - mediaType responses: '200': description: Template preview generated successfully content: application/json: schema: type: object properties: status: type: string example: "success" preview: type: string example: "Movie Trakt Collection" '400': description: Bad request - invalid template or parameters '500': description: Failed to generate template preview /collections/fetch-title: post: summary: Quick validation and title extraction from external collection URL description: Performs quick validation (first 10 items) and extracts the title from external collection URLs (Trakt, TMDb, IMDb, Letterboxd). Returns immediately with basic media type detection for fast UX. For comprehensive media type analysis, use the /collections/detect-media-type endpoint. tags: - collection requestBody: required: true content: application/json: schema: type: object properties: url: type: string example: "https://trakt.tv/users/username/lists/my-list" type: type: string enum: [trakt, tmdb, imdb, letterboxd] example: "trakt" required: - url - type responses: '200': description: Title fetched successfully content: application/json: schema: type: object properties: status: type: string example: "success" title: type: string example: "My List" mediaType: type: string enum: [movie, tv, both] example: "movie" '400': description: Bad request - invalid URL or unsupported type '429': description: Too many requests - rate limited '500': description: Failed to fetch title /collections/detect-media-type: post: summary: Comprehensive media type detection from external collection URL description: Performs comprehensive analysis (up to 1000 items) to accurately detect media types from external collection URLs. This endpoint runs in the background after quick title validation to provide detailed media type information for better user guidance. tags: - collection requestBody: required: true content: application/json: schema: type: object properties: url: type: string example: "https://trakt.tv/users/username/lists/my-list" type: type: string enum: [trakt, tmdb, imdb, letterboxd] example: "trakt" required: - url - type responses: '200': description: Media type detected successfully content: application/json: schema: type: object properties: status: type: string example: "success" mediaType: type: string enum: [movie, tv, both] example: "both" description: "Detected media type based on comprehensive list analysis" '400': description: Bad request - invalid URL, unsupported type, or failed to analyze list content '429': description: Too many requests - rate limited '500': description: Internal server error while detecting media type /collections/poster: post: summary: Upload a poster image for collections description: Uploads a custom poster image that can be used for collections. Supports JPEG, PNG, and WebP formats. tags: - collection requestBody: required: true content: multipart/form-data: schema: type: object properties: poster: type: string format: binary description: The poster image file required: - poster responses: '200': description: Poster uploaded successfully content: application/json: schema: type: object properties: filename: type: string example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" url: type: string example: "/api/v1/collections/poster/f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" '400': description: Bad request - invalid file or upload error '403': description: Authentication required '500': description: Internal server error /collections/generate-poster: post: summary: Generate a poster for a collection description: Generates a custom poster based on collection configuration data. Creates text-based posters with collection type styling. tags: - collection security: - cookieAuth: [] - apiKeyAuth: [] requestBody: required: true content: application/json: schema: type: object properties: collectionName: type: string description: Name of the collection to generate poster for example: "Top Trakt Movies" collectionType: type: string description: Type of collection (trakt, tmdb, imdb, letterboxd, tautulli, overseerr, hub) example: "trakt" collectionSubtype: type: string description: Subtype of the collection example: "popular" mediaType: type: string enum: [movie, tv] description: Media type for the collection example: "movie" template: type: string description: Collection template for additional context example: "Top {count} {type} from Trakt" required: - collectionName responses: '200': description: Poster generated successfully content: application/json: schema: type: object properties: filename: type: string example: "generated_abc123def.jpg" url: type: string example: "/api/v1/collections/poster/generated_abc123def.jpg" message: type: string example: "Poster generated successfully" '400': description: Bad request - missing or invalid collection name '401': description: Authentication required '500': description: Internal server error /collections/download-poster: post: summary: Download a poster from a URL description: Downloads a poster image from a given URL and saves it to the system. Supports JPEG, PNG, and WebP formats with automatic validation and processing. tags: - collection security: - cookieAuth: [] - apiKeyAuth: [] requestBody: required: true content: application/json: schema: type: object properties: url: type: string format: uri description: URL of the poster image to download example: "https://example.com/poster.jpg" required: - url responses: '200': description: Poster downloaded successfully content: application/json: schema: type: object properties: filename: type: string example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" url: type: string example: "/api/v1/collections/poster/f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" message: type: string example: "Poster downloaded successfully" '400': description: Bad request - invalid URL, unsupported format, or file too large content: application/json: schema: type: object properties: error: type: string example: "Invalid URL format" '401': description: Authentication required '429': description: Too many requests - rate limit exceeded content: application/json: schema: type: object properties: error: type: string example: "Too many requests. Please try again later." '500': description: Internal server error /collections/posters: get: summary: List all stored poster files description: Returns a list of all poster files currently stored in the system with their URLs. tags: - collection security: - cookieAuth: [] - apiKeyAuth: [] responses: '200': description: List of poster files content: application/json: schema: type: object properties: posters: type: array items: type: object properties: filename: type: string description: The filename of the poster example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" url: type: string description: The URL to access the poster example: "/api/v1/collections/poster/f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" '401': description: Authentication required '500': description: Internal server error /collections/poster/{filename}: get: summary: Serve poster image description: Serves a poster image by filename. No authentication required. tags: - collection parameters: - in: path name: filename required: true schema: type: string example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" responses: '200': description: Poster image served successfully content: image/jpeg: schema: type: string format: binary '404': description: Poster not found '500': description: Internal server error delete: summary: Delete a poster image description: Deletes a poster image by filename. Authentication required. tags: - collection parameters: - in: path name: filename required: true schema: type: string example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" responses: '200': description: Poster deleted successfully content: application/json: schema: type: object properties: message: type: string example: "Poster deleted successfully" '403': description: Authentication required '404': description: Poster not found '500': description: Internal server error /collections/cleanup-missing: delete: summary: Clean up missing collections description: Removes all collection configurations where missing is true (collections that no longer exist in Plex) and also deletes them from Plex hubs if they are present there. tags: - collection responses: '200': description: Missing collections cleaned up successfully content: application/json: schema: type: object properties: message: type: string example: "3 missing collection configurations removed successfully (2 also deleted from Plex hubs)" cleanupCount: type: integer description: "Number of configurations that were removed" example: 3 hubDeleteCount: type: integer description: "Number of collections that were deleted from Plex hubs" example: 2 '403': description: Authentication required '500': description: Failed to cleanup missing collections content: application/json: schema: type: object properties: error: type: string example: "Failed to cleanup missing collections" /collections/networks/countries: get: summary: Get available countries for Networks collections description: Returns all available countries/regions that can be used for Networks streaming platform collections. tags: - collection responses: '200': description: Countries retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/NetworksCountryOption' '500': description: Failed to fetch countries content: application/json: schema: type: object properties: error: type: string example: "Failed to load available countries" /collections/networks/platforms: get: summary: Get available platforms for a country description: Returns all available streaming platforms for the specified country/region. tags: - collection parameters: - name: country in: query required: true schema: type: string description: Country code to get platforms for example: "united-states" responses: '200': description: Platforms retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/NetworksPlatformOption' '400': description: Country parameter is required content: application/json: schema: type: object properties: error: type: string example: "Country parameter is required" '500': description: Failed to fetch platforms content: application/json: schema: type: object properties: error: type: string example: "Failed to load platforms for country" /collections/preexisting: get: summary: Get pre-existing Plex collections description: Returns all Plex collections that were not created by Agregarr (collections without Agregarr labels). tags: - collection responses: '200': description: Pre-existing collections retrieved successfully content: application/json: schema: type: object properties: collections: type: array items: type: object properties: id: type: string example: "12345" name: type: string example: "My Collection" summary: type: string example: "A collection of my favorite movies" libraryId: type: string example: "1" libraryTitle: type: string example: "Movies" itemCount: type: number example: 25 thumb: type: string example: "/library/metadata/12345/thumb" art: type: string example: "/library/metadata/12345/art" guid: type: string example: "com.plexapp.agents.none://12345" updatedAt: type: number example: 1640995200 addedAt: type: number example: 1640995200 labels: type: array items: type: object '400': description: Bad request - Plex server not configured or no admin token '500': description: Failed to fetch pre-existing collections /defaulthubs: get: summary: Get default Plex hub configurations description: Returns all default Plex hub configurations. tags: - collection responses: '200': description: Default hub configurations retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/PlexHubConfig' '500': description: Failed to retrieve hub configurations post: summary: Save default Plex hub configurations description: Updates default Plex hub configurations with the provided values. tags: - collection requestBody: required: true content: application/json: schema: type: object properties: hubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' responses: '200': description: Hub configurations saved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/PlexHubConfig' '500': description: Failed to save hub configurations /defaulthubs/{id}/settings: put: summary: Update individual default hub settings description: Updates settings for a specific default hub while preserving computed fields. tags: - collection parameters: - name: id in: path required: true description: Default hub ID schema: type: string example: "1-movie.recentlyadded" requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PlexHubConfigUpdate' responses: '200': description: Default hub settings updated successfully content: application/json: schema: type: object properties: hubConfig: $ref: '#/components/schemas/PlexHubConfig' message: type: string example: "Default hub settings updated successfully" '404': description: Default hub not found '500': description: Failed to update default hub settings /defaulthubs/discover: post: summary: Discover new default hubs description: Discovery operation for new default hubs (replaces /append for discovery). tags: - collection requestBody: required: true content: application/json: schema: type: object properties: hubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfigForDiscovery' responses: '200': description: Default hubs discovered successfully content: application/json: schema: type: object properties: hubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' message: type: string example: "Default hubs discovered successfully" '500': description: Failed to discover default hubs /defaulthubs/append: post: summary: Append default Plex hub configurations description: Appends new default hub configurations to existing ones (for discovery). tags: - collection requestBody: required: true content: application/json: schema: type: object properties: hubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' responses: '200': description: Hub configurations appended successfully content: application/json: schema: type: object properties: hubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' message: type: string example: 'Default hub configurations appended successfully' '400': description: Invalid request body '500': description: Failed to append hub configurations /preexisting: get: summary: Get pre-existing collection configurations description: Returns all pre-existing Plex collection configurations. tags: - collection responses: '200': description: Pre-existing collection configurations retrieved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/PreExistingCollectionConfig' '500': description: Failed to retrieve pre-existing collection configurations post: summary: Save pre-existing collection configurations description: Updates pre-existing collection configurations with the provided values. tags: - collection requestBody: required: true content: application/json: schema: type: object properties: preExistingConfigs: type: array items: $ref: '#/components/schemas/PreExistingCollectionConfig' responses: '200': description: Pre-existing collection configurations saved successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/PreExistingCollectionConfig' '500': description: Failed to save pre-existing collection configurations /preexisting/{id}/settings: put: summary: Update individual pre-existing collection settings description: Updates settings for a specific pre-existing collection while preserving computed fields. tags: - collection parameters: - name: id in: path required: true description: Pre-existing collection ID schema: type: string example: "1-35954" requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PreExistingCollectionConfigUpdate' responses: '200': description: Pre-existing collection settings updated successfully content: application/json: schema: type: object properties: preExistingCollectionConfig: $ref: '#/components/schemas/PreExistingCollectionConfig' message: type: string example: "Pre-existing collection settings updated successfully" '404': description: Pre-existing collection not found '500': description: Failed to update pre-existing collection settings /preexisting/{id}/promote: patch: summary: Promote pre-existing collection to top section description: Moves a pre-existing collection from the A-Z alphabetical section to the promoted section with custom ordering. tags: - collection parameters: - name: id in: path required: true description: Pre-existing collection ID schema: type: string example: "1-35954" responses: '200': description: Pre-existing collection promoted successfully content: application/json: schema: type: object properties: success: type: boolean example: true config: $ref: '#/components/schemas/PreExistingCollectionConfig' '400': description: Collection is already in promoted section content: application/json: schema: type: object properties: error: type: string example: "Collection is already in promoted section" '404': description: Pre-existing collection not found content: application/json: schema: type: object properties: error: type: string example: "Pre-existing collection not found" '500': description: Failed to promote pre-existing collection content: application/json: schema: type: object properties: error: type: string example: "Failed to promote pre-existing collection" /preexisting/{id}/demote: patch: summary: Demote pre-existing collection to A-Z section description: Moves a pre-existing collection from the promoted section to the A-Z alphabetical section. tags: - collection parameters: - name: id in: path required: true description: Pre-existing collection ID schema: type: string example: "1-35954" responses: '200': description: Pre-existing collection demoted successfully content: application/json: schema: type: object properties: success: type: boolean example: true config: $ref: '#/components/schemas/PreExistingCollectionConfig' '400': description: Collection is already in A-Z section content: application/json: schema: type: object properties: error: type: string example: "Collection is already in A-Z section" '404': description: Pre-existing collection not found content: application/json: schema: type: object properties: error: type: string example: "Pre-existing collection not found" '500': description: Failed to demote pre-existing collection content: application/json: schema: type: object properties: error: type: string example: "Failed to demote pre-existing collection" /preexisting/discover: post: summary: Discover new pre-existing collections description: Discovery operation for new pre-existing collections (replaces /append for discovery). tags: - collection requestBody: required: true content: application/json: schema: type: object properties: preExistingCollectionConfigs: type: array items: $ref: '#/components/schemas/PreExistingCollectionConfigForDiscovery' responses: '200': description: Pre-existing collections discovered successfully content: application/json: schema: type: object properties: preExistingCollectionConfigs: type: array items: $ref: '#/components/schemas/PreExistingCollectionConfig' message: type: string example: "Pre-existing collections discovered successfully" '500': description: Failed to discover pre-existing collections /preexisting/append: post: summary: Append pre-existing collection configurations description: Appends new pre-existing collection configurations to existing ones (for discovery). tags: - collection requestBody: required: true content: application/json: schema: type: object properties: preExistingCollectionConfigs: type: array items: $ref: '#/components/schemas/DiscoveredCollectionConfig' responses: '200': description: Pre-existing collection configurations appended successfully content: application/json: schema: type: object properties: preExistingCollectionConfigs: type: array items: $ref: '#/components/schemas/PreExistingCollectionConfig' message: type: string example: 'Pre-existing collection configurations appended successfully' '400': description: Invalid request body '500': description: Failed to append pre-existing collection configurations /reorder: post: summary: True unified reordering for mixed collection lists description: Reorders mixed collection lists in a single operation, eliminating race conditions and multiple API calls. tags: - collection requestBody: required: true content: application/json: schema: type: object properties: libraryId: type: string description: "Plex library ID where reordering occurs" example: "1" mixedItems: type: array description: "Array of mixed collection items in their new order" items: type: object properties: id: type: string description: "Item ID" configType: type: string enum: ['collection', 'hub', 'preExisting'] description: "Type of collection item" position: type: number description: "Position in the mixed list" additionalProperties: true required: - id - configType - position context: type: string enum: ['home', 'recommended', 'library'] description: "Context determining which sortOrder field to update" example: 'home' required: - libraryId - mixedItems - context responses: '200': description: Items reordered successfully content: application/json: schema: type: object properties: success: type: boolean example: true message: type: string example: 'Successfully reordered 3 collections' updatedItems: type: array description: "Items with updated sort orders" items: oneOf: - $ref: '#/components/schemas/CollectionConfig' - $ref: '#/components/schemas/PlexHubConfig' - $ref: '#/components/schemas/PreExistingCollectionConfig' '400': description: Invalid request parameters '500': description: Failed to reorder items /settings/main: get: summary: Get main settings description: Retrieves all main settings in a JSON object. tags: - settings responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MainSettings' post: summary: Update main settings description: Updates main settings with the provided values. tags: - settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MainSettings' responses: '200': description: 'Values were sucessfully updated' content: application/json: schema: $ref: '#/components/schemas/MainSettings' /settings/main/regenerate: post: summary: Get main settings with newly-generated API key description: Returns main settings in a JSON object, using the new API key. tags: - settings responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MainSettings' /settings/plex: get: summary: Get Plex settings description: Retrieves current Plex settings. tags: - settings responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/PlexSettings' post: summary: Update Plex settings description: Updates Plex settings with the provided values. tags: - settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PlexSettings' responses: '200': description: 'Values were successfully updated' content: application/json: schema: $ref: '#/components/schemas/PlexSettings' /settings/plex/libraries: get: summary: Get Plex libraries directly from Plex server description: Fetches all available libraries directly from the connected Plex server. tags: - settings responses: '200': description: Plex libraries retrieved successfully content: application/json: schema: type: array items: type: object properties: id: type: string description: Library ID from Plex example: "1" name: type: string description: Library name example: "Movies" type: type: string enum: [movie, show] description: Library media type example: "movie" enabled: type: boolean description: Always true for direct Plex libraries example: true '400': description: No admin Plex token found '500': description: Failed to fetch Plex libraries /settings/plex/library: get: summary: Get Plex libraries description: Returns a list of Plex libraries in a JSON array. tags: - settings parameters: - in: query name: sync description: Syncs the current libraries with the current Plex server schema: type: string nullable: true responses: '200': description: 'Plex libraries returned' content: application/json: schema: type: array items: $ref: '#/components/schemas/PlexLibrary' /settings/plex/sync: get: summary: Get status of full Plex library scan description: Returns scan progress in a JSON array. tags: - settings responses: '200': description: Status of Plex scan content: application/json: schema: type: object properties: running: type: boolean example: false progress: type: number example: 0 total: type: number example: 100 currentLibrary: $ref: '#/components/schemas/PlexLibrary' libraries: type: array items: $ref: '#/components/schemas/PlexLibrary' post: summary: Start full Plex library scan description: Runs a full Plex library scan and returns the progress in a JSON array. tags: - settings requestBody: content: application/json: schema: type: object properties: cancel: type: boolean example: false start: type: boolean example: false responses: '200': description: Status of Plex scan content: application/json: schema: type: object properties: running: type: boolean example: false progress: type: number example: 0 total: type: number example: 100 currentLibrary: $ref: '#/components/schemas/PlexLibrary' libraries: type: array items: $ref: '#/components/schemas/PlexLibrary' /settings/plex/devices/servers: get: summary: Gets the user's available Plex servers description: Returns a list of available Plex servers and their connectivity state tags: - settings responses: '200': description: OK content: application/json: schema: type: array items: $ref: '#/components/schemas/PlexDevice' /settings/plex/users: get: summary: Get Plex users description: | Returns a list of Plex users in a JSON array. Requires the `MANAGE_USERS` permission. tags: - settings - users responses: '200': description: Plex users content: application/json: schema: type: array items: type: object properties: id: type: string title: type: string username: type: string email: type: string thumb: type: string /settings/collections/poster: post: summary: Upload collection poster image description: | Uploads a custom poster image for collections. The image will be processed, optimized, and stored securely. **Important: Sync-Based Behavior** - Posters are only validated and stored when uploaded - Posters are applied to Plex collections during the next collection sync run - Changes are not immediately visible in Plex until sync occurs **Security Features:** - Admin-only access required - File type validation (JPEG, PNG, WebP only) - File size limit (10MB maximum) - Automatic image processing and optimization - Secure filename generation using UUIDs **Processing:** - Images are automatically resized to 500x750 pixels (2:3 aspect ratio) - All images are converted to JPEG format for consistency - Quality optimization applied (85% quality) tags: - settings security: - cookieAuth: [] - apiKeyAuth: [] requestBody: required: true content: multipart/form-data: schema: type: object properties: poster: type: string format: binary description: The poster image file (JPEG, PNG, or WebP, max 10MB) required: - poster responses: '200': description: Poster uploaded successfully content: application/json: schema: type: object properties: filename: type: string description: The generated filename for the uploaded poster example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" url: type: string description: The URL to access the uploaded poster example: "/api/v1/settings/collections/poster/f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" '400': description: Bad request - Invalid file or upload error content: application/json: schema: type: object properties: error: type: string enum: - "No file uploaded" - "Invalid file type. Only JPEG, PNG, and WebP are allowed." - "File extension does not match file type." - "File size too large. Maximum size: 10MB" - "Invalid image format detected" - "Image dimensions too large" - "Failed to process poster" example: "Invalid file type. Only JPEG, PNG, and WebP are allowed." '403': description: Insufficient permissions content: application/json: schema: type: object properties: error: type: string example: "Insufficient permissions" '500': description: Internal server error content: application/json: schema: type: object properties: error: type: string example: "Internal server error" /settings/collections/poster/{filename}: get: summary: Serve collection poster image description: | Serves a collection poster image by filename. Images are cached for optimal performance. **Security Features:** - Filename validation to prevent path traversal attacks - UUID-based filenames make guessing difficult - No authentication required (images are already admin-uploaded) **Caching:** - Images are cached for 1 year for optimal performance - Content-Type is always image/jpeg (all images are converted) tags: - settings parameters: - name: filename in: path required: true description: The filename of the poster image (UUID format with extension) schema: type: string pattern: '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\.(jpg|jpeg|png|webp)$' example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" responses: '200': description: Poster image served successfully content: image/jpeg: schema: type: string format: binary headers: Cache-Control: description: Caching directive schema: type: string example: "public, max-age=31536000" Content-Type: description: Content type header schema: type: string example: "image/jpeg" '400': description: Invalid filename format content: application/json: schema: type: object properties: error: type: string example: "Invalid filename" '404': description: Poster not found content: application/json: schema: type: object properties: error: type: string example: "Poster not found" '500': description: Internal server error content: application/json: schema: type: object properties: error: type: string example: "Failed to serve poster" delete: summary: Delete collection poster image description: | Deletes a collection poster image by filename. Requires admin permissions. **Important: Sync-Based Behavior** - Poster file is immediately deleted from storage - Collections using this poster will revert to default during the next collection sync run - Changes are not immediately visible in Plex until sync occurs **Security Features:** - Admin-only access required - Filename validation to prevent path traversal attacks - Safe deletion with error handling tags: - settings security: - cookieAuth: [] - apiKeyAuth: [] parameters: - name: filename in: path required: true description: The filename of the poster image to delete schema: type: string pattern: '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\.(jpg|jpeg|png|webp)$' example: "f47ac10b-58cc-4372-a567-0e02b2c3d479.jpg" responses: '200': description: Poster deleted successfully content: application/json: schema: type: object properties: message: type: string example: "Poster deleted successfully" '400': description: Invalid filename format content: application/json: schema: type: object properties: error: type: string example: "Invalid filename" '403': description: Insufficient permissions content: application/json: schema: type: object properties: error: type: string example: "Insufficient permissions" '404': description: Poster not found content: application/json: schema: type: object properties: error: type: string example: "Poster not found" '500': description: Internal server error content: application/json: schema: type: object properties: error: type: string example: "Internal server error" /settings/tautulli: get: summary: Get Tautulli settings description: Retrieves current Tautulli settings. tags: - settings responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/TautulliSettings' post: summary: Update Tautulli settings description: Updates Tautulli settings with the provided values. tags: - settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TautulliSettings' responses: '200': description: 'Values were successfully updated' content: application/json: schema: $ref: '#/components/schemas/TautulliSettings' /settings/tautulli/test: post: summary: Test Tautulli connection description: Tests connection to a Tautulli instance. tags: - settings requestBody: required: true content: application/json: schema: type: object properties: hostname: type: string example: 'tautulli.example.com' port: type: number example: 8181 apiKey: type: string example: 'your-api-key-here' useSsl: type: boolean example: false urlBase: type: string example: '/tautulli' required: - hostname - apiKey responses: '200': description: 'Connection test successful' content: application/json: schema: type: object properties: success: type: boolean example: true version: type: string example: '2.13.4' '400': description: 'Missing required parameters' '500': description: 'Connection test failed' /settings/trakt: get: summary: Get Trakt settings description: Retrieves current Trakt settings. tags: - settings responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/TraktSettings' post: summary: Update Trakt settings description: Updates Trakt settings with the provided values. tags: - settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TraktSettings' responses: '200': description: 'Values were successfully updated' content: application/json: schema: $ref: '#/components/schemas/TraktSettings' /settings/trakt/test: post: summary: Test Trakt connection description: Tests connection to Trakt API with the provided API key. tags: - settings requestBody: required: true content: application/json: schema: type: object properties: apiKey: type: string description: Trakt API key (Client ID) required: - apiKey responses: '200': description: 'Connection test successful' content: application/json: schema: type: object properties: success: type: boolean example: true '400': description: 'Missing required parameters' '500': description: 'Connection test failed' /settings/overseerr: get: summary: Get Overseerr settings description: Retrieves current Overseerr connection settings for external instances. tags: - settings responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OverseerrSettings' post: summary: Update Overseerr settings description: Updates Overseerr connection settings and tests the connection. tags: - settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OverseerrSettings' responses: '200': description: 'Values were successfully updated' content: application/json: schema: $ref: '#/components/schemas/OverseerrSettings' '500': description: 'Connection test failed' /overseerr/test: post: summary: Test Overseerr connection description: Tests connection to an external Overseerr instance. tags: - other requestBody: required: true content: application/json: schema: type: object properties: hostname: type: string example: 'overseerr.example.com' port: type: number example: 5055 apiKey: type: string example: 'your-api-key-here' useSsl: type: boolean example: false urlBase: type: string example: '/overseerr' required: - hostname - apiKey responses: '200': description: 'Connection test successful' content: application/json: schema: type: object properties: success: type: boolean example: true version: type: string example: '1.33.2' '400': description: 'Missing required parameters' '500': description: 'Connection test failed' /settings/serviceuser: get: summary: Get service user settings description: Returns service user creation settings. tags: - settings responses: '200': description: 'Service user settings returned successfully' content: application/json: schema: $ref: '#/components/schemas/ServiceUserSettings' post: summary: Update service user settings description: Updates service user creation settings. tags: - settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ServiceUserSettings' responses: '200': description: 'Service user settings updated successfully' content: application/json: schema: $ref: '#/components/schemas/ServiceUserSettings' '500': description: 'Settings update failed' /settings/radarr: get: summary: Get Radarr settings description: Returns all Radarr settings in a JSON array. tags: - settings responses: '200': description: 'Values were returned' content: application/json: schema: type: array items: $ref: '#/components/schemas/RadarrSettings' post: summary: Create Radarr instance description: Creates a new Radarr instance from the request body. tags: - settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RadarrSettings' responses: '201': description: 'New Radarr instance created' content: application/json: schema: $ref: '#/components/schemas/RadarrSettings' /settings/radarr/test: post: summary: Test Radarr configuration description: Tests if the Radarr configuration is valid. Returns profiles and root folders on success. tags: - settings requestBody: required: true content: application/json: schema: type: object properties: hostname: type: string example: '127.0.0.1' port: type: number example: 7878 apiKey: type: string example: yourapikey useSsl: type: boolean example: false baseUrl: type: string required: - hostname - apiKey responses: '200': description: Succesfully connected to Radarr instance content: application/json: schema: type: object properties: profiles: type: array items: $ref: '#/components/schemas/ServiceProfile' /settings/radarr/{radarrId}: put: summary: Update Radarr instance description: Updates an existing Radarr instance with the provided values. tags: - settings parameters: - in: path name: radarrId required: true schema: type: integer description: Radarr instance ID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RadarrSettings' responses: '200': description: 'Radarr instance updated' content: application/json: schema: $ref: '#/components/schemas/RadarrSettings' delete: summary: Delete Radarr instance description: Deletes an existing Radarr instance based on the radarrId parameter. tags: - settings parameters: - in: path name: radarrId required: true schema: type: integer description: Radarr instance ID responses: '200': description: 'Radarr instance updated' content: application/json: schema: $ref: '#/components/schemas/RadarrSettings' /settings/radarr/{radarrId}/profiles: get: summary: Get available Radarr profiles description: Returns a list of profiles available on the Radarr server instance in a JSON array. tags: - settings parameters: - in: path name: radarrId required: true schema: type: integer description: Radarr instance ID responses: '200': description: Returned list of profiles content: application/json: schema: type: array items: $ref: '#/components/schemas/ServiceProfile' /settings/sonarr: get: summary: Get Sonarr settings description: Returns all Sonarr settings in a JSON array. tags: - settings responses: '200': description: 'Values were returned' content: application/json: schema: type: array items: $ref: '#/components/schemas/SonarrSettings' post: summary: Create Sonarr instance description: Creates a new Sonarr instance from the request body. tags: - settings requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SonarrSettings' responses: '201': description: 'New Sonarr instance created' content: application/json: schema: $ref: '#/components/schemas/SonarrSettings' /settings/sonarr/test: post: summary: Test Sonarr configuration description: Tests if the Sonarr configuration is valid. Returns profiles and root folders on success. tags: - settings requestBody: required: true content: application/json: schema: type: object properties: hostname: type: string example: '127.0.0.1' port: type: number example: 8989 apiKey: type: string example: yourapikey useSsl: type: boolean example: false baseUrl: type: string required: - hostname - apiKey responses: '200': description: Succesfully connected to Sonarr instance content: application/json: schema: type: object properties: profiles: type: array items: $ref: '#/components/schemas/ServiceProfile' /settings/sonarr/{sonarrId}: put: summary: Update Sonarr instance description: Updates an existing Sonarr instance with the provided values. tags: - settings parameters: - in: path name: sonarrId required: true schema: type: integer description: Sonarr instance ID requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SonarrSettings' responses: '200': description: 'Sonarr instance updated' content: application/json: schema: $ref: '#/components/schemas/SonarrSettings' delete: summary: Delete Sonarr instance description: Deletes an existing Sonarr instance based on the sonarrId parameter. tags: - settings parameters: - in: path name: sonarrId required: true schema: type: integer description: Sonarr instance ID responses: '200': description: 'Sonarr instance updated' content: application/json: schema: $ref: '#/components/schemas/SonarrSettings' /settings/public: get: summary: Get public settings security: [] description: Returns settings that are not protected or sensitive. Mainly used to determine if the application has been configured for the first time. tags: - settings responses: '200': description: Public settings returned content: application/json: schema: $ref: '#/components/schemas/PublicSettings' /settings/initialize: post: summary: Initialize application description: Sets the app as initialized, allowing the user to navigate to pages other than the setup page. tags: - settings responses: '200': description: Public settings returned content: application/json: schema: $ref: '#/components/schemas/PublicSettings' /settings/jobs: get: summary: Get scheduled jobs description: Returns list of all scheduled jobs and details about their next execution time in a JSON array. tags: - settings responses: '200': description: Scheduled jobs returned content: application/json: schema: type: array items: $ref: '#/components/schemas/Job' /settings/jobs/{jobId}/run: post: summary: Invoke a specific job description: Invokes a specific job to run. Will return the new job status in JSON format. tags: - settings parameters: - in: path name: jobId required: true schema: type: string responses: '200': description: Invoked job returned content: application/json: schema: $ref: '#/components/schemas/Job' /settings/jobs/{jobId}/cancel: post: summary: Cancel a specific job description: Cancels a specific job. Will return the new job status in JSON format. tags: - settings parameters: - in: path name: jobId required: true schema: type: string responses: '200': description: Canceled job returned content: application/json: schema: $ref: '#/components/schemas/Job' /settings/jobs/{jobId}/schedule: post: summary: Modify job schedule description: Re-registers the job with the schedule specified. Will return the job in JSON format. tags: - settings parameters: - in: path name: jobId required: true schema: type: string requestBody: required: true content: application/json: schema: type: object properties: schedule: type: string example: '0 */5 * * * *' responses: '200': description: Rescheduled job content: application/json: schema: $ref: '#/components/schemas/Job' /settings/cache: get: summary: Get a list of active caches description: Retrieves a list of all active caches and their current stats. tags: - settings responses: '200': description: Caches returned content: application/json: schema: type: object properties: imageCache: type: object properties: tmdb: type: object properties: size: type: number example: 123456 imageCount: type: number example: 123 apiCaches: type: array items: type: object properties: id: type: string example: cache-id name: type: string example: cache name stats: type: object properties: hits: type: number misses: type: number keys: type: number ksize: type: number vsize: type: number /settings/cache/{cacheId}/flush: post: summary: Flush a specific cache description: Flushes all data from the cache ID provided tags: - settings parameters: - in: path name: cacheId required: true schema: type: string responses: '204': description: 'Flushed cache' /settings/logs: get: summary: Returns logs description: Returns list of all log items and details tags: - settings parameters: - in: query name: take schema: type: number nullable: true example: 25 - in: query name: skip schema: type: number nullable: true example: 0 - in: query name: filter schema: type: string nullable: true enum: [debug, info, warn, error] default: debug - in: query name: search schema: type: string nullable: true example: plex responses: '200': description: Server log returned content: application/json: schema: type: array items: type: object properties: label: type: string example: server level: type: string example: info message: type: string example: Server ready on port 5055 timestamp: type: string example: '2020-12-15T16:20:00.069Z' /settings/about: get: summary: Get server stats description: Returns current server stats in a JSON object. tags: - settings responses: '200': description: Returned about settings content: application/json: schema: type: object properties: version: type: string example: '1.0.0' totalRequests: type: number example: 100 totalMediaItems: type: number example: 100 tz: type: string nullable: true example: Asia/Tokyo appDataPath: type: string example: /app/config /auth/me: get: summary: Get logged-in user description: Returns the currently logged-in user. tags: - auth - users responses: '200': description: Object containing the logged-in user in JSON content: application/json: schema: $ref: '#/components/schemas/User' /auth/plex: post: summary: Sign in using a Plex token description: Takes an `authToken` (Plex token) to log the user in. Generates a session cookie for use in further requests. If the user does not exist, and there are no other users, then a user will be created with full admin privileges. If a user logs in with access to the main Plex server, they will also have an account created, but without any permissions. security: [] tags: - auth responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/User' requestBody: required: true content: application/json: schema: type: object properties: authToken: type: string required: - authToken /auth/local: post: summary: Sign in using a local account description: Takes an `email` and a `password` to log the user in. Generates a session cookie for use in further requests. security: [] tags: - auth responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/User' requestBody: required: true content: application/json: schema: type: object properties: email: type: string password: type: string required: - email - password /auth/logout: post: summary: Sign out and clear session cookie description: Completely clear the session cookie and associated values, effectively signing the user out. tags: - auth responses: '200': description: OK content: application/json: schema: type: object properties: status: type: string example: 'ok' /auth/reset-password: post: summary: Send a reset password email description: Sends a reset password email to the email if the user exists security: [] tags: - users responses: '200': description: OK content: application/json: schema: type: object properties: status: type: string example: 'ok' requestBody: required: true content: application/json: schema: type: object properties: email: type: string required: - email /auth/reset-password/{guid}: post: summary: Reset the password for a user description: Resets the password for a user if the given guid is connected to a user security: [] tags: - users parameters: - in: path name: guid required: true schema: type: string example: '9afef5a7-ec89-4d5f-9397-261e96970b50' responses: '200': description: OK content: application/json: schema: type: object properties: status: type: string example: 'ok' requestBody: required: true content: application/json: schema: type: object properties: password: type: string required: - password /user: get: summary: Get all users description: Returns all users in a JSON object. tags: - users parameters: - in: query name: take schema: type: number nullable: true example: 20 - in: query name: skip schema: type: number nullable: true example: 0 - in: query name: sort schema: type: string enum: [created, updated, requests, displayname] default: created responses: '200': description: A JSON array of all users content: application/json: schema: type: object properties: pageInfo: $ref: '#/components/schemas/PageInfo' results: type: array items: $ref: '#/components/schemas/User' post: summary: Create new user description: | Creates a new user. Requires the `MANAGE_USERS` permission. tags: - users requestBody: required: true content: application/json: schema: type: object properties: email: type: string example: 'hey@itsme.com' username: type: string permissions: type: number responses: '201': description: The created user content: application/json: schema: $ref: '#/components/schemas/User' put: summary: Update batch of users description: | Update users with given IDs with provided values in request `body.settings`. You cannot update users' Plex tokens through this request. Requires the `MANAGE_USERS` permission. tags: - users requestBody: required: true content: application/json: schema: type: object properties: ids: type: array items: type: integer permissions: type: integer responses: '200': description: Successfully updated user details content: application/json: schema: type: array items: $ref: '#/components/schemas/User' /user/import-from-plex: post: summary: Import all users from Plex description: | Fetches and imports users from the Plex server. If a list of Plex IDs is provided in the request body, only the specified users will be imported. Otherwise, all users will be imported. Requires the `MANAGE_USERS` permission. tags: - users requestBody: required: false content: application/json: schema: type: object properties: plexIds: type: array items: type: string responses: '201': description: A list of the newly created users content: application/json: schema: type: array items: $ref: '#/components/schemas/User' /movie/{movieId}: get: summary: Get movie details description: Returns full movie details in a JSON object. tags: - movies parameters: - in: path name: movieId required: true schema: type: number example: 337401 - in: query name: language schema: type: string example: en responses: '200': description: Movie details content: application/json: schema: $ref: '#/components/schemas/MovieDetails' /movie/{movieId}/recommendations: get: summary: Get recommended movies description: Returns list of recommended movies based on provided movie ID in a JSON object. tags: - movies parameters: - in: path name: movieId required: true schema: type: number example: 337401 - in: query name: page schema: type: number example: 1 default: 1 - in: query name: language schema: type: string example: en responses: '200': description: List of movies content: application/json: schema: type: object properties: page: type: number example: 1 totalPages: type: number example: 20 totalResults: type: number example: 200 results: type: array items: $ref: '#/components/schemas/MovieResult' /movie/{movieId}/similar: get: summary: Get similar movies description: Returns list of similar movies based on the provided movieId in a JSON object. tags: - movies parameters: - in: path name: movieId required: true schema: type: number example: 337401 - in: query name: page schema: type: number example: 1 default: 1 - in: query name: language schema: type: string example: en responses: '200': description: List of movies content: application/json: schema: type: object properties: page: type: number example: 1 totalPages: type: number example: 20 totalResults: type: number example: 200 results: type: array items: $ref: '#/components/schemas/MovieResult' /movie/{movieId}/ratings: get: summary: Get movie ratings description: Returns ratings based on the provided movieId in a JSON object. tags: - movies parameters: - in: path name: movieId required: true schema: type: number example: 337401 responses: '200': description: Ratings returned content: application/json: schema: type: object properties: title: type: string example: Mulan year: type: number example: 2020 url: type: string example: 'http://www.rottentomatoes.com/m/mulan_2020/' criticsScore: type: number example: 85 criticsRating: type: string enum: ['Rotten', 'Fresh', 'Certified Fresh'] audienceScore: type: number example: 65 audienceRating: type: string enum: ['Spilled', 'Upright'] /movie/{movieId}/ratingscombined: get: summary: Get RT and IMDB movie ratings combined description: Returns ratings from RottenTomatoes and IMDB based on the provided movieId in a JSON object. tags: - movies parameters: - in: path name: movieId required: true schema: type: number example: 337401 responses: '200': description: Ratings returned content: application/json: schema: type: object properties: rt: type: object properties: title: type: string example: Mulan year: type: number example: 2020 url: type: string example: 'http://www.rottentomatoes.com/m/mulan_2020/' criticsScore: type: number example: 85 criticsRating: type: string enum: ['Rotten', 'Fresh', 'Certified Fresh'] audienceScore: type: number example: 65 audienceRating: type: string enum: ['Spilled', 'Upright'] imdb: type: object properties: title: type: string example: I am Legend url: type: string example: 'https://www.imdb.com/title/tt0480249' criticsScore: type: number example: 6.5 /tv/{tvId}: get: summary: Get TV details description: Returns full TV details in a JSON object. tags: - tv parameters: - in: path name: tvId required: true schema: type: number example: 76479 - in: query name: language schema: type: string example: en responses: '200': description: TV details content: application/json: schema: $ref: '#/components/schemas/TvDetails' /tv/{tvId}/season/{seasonId}: get: summary: Get season details and episode list description: Returns season details with a list of episodes in a JSON object. tags: - tv parameters: - in: path name: tvId required: true schema: type: number example: 76479 - in: path name: seasonId required: true schema: type: number example: 1 - in: query name: language schema: type: string example: en responses: '200': description: TV details content: application/json: schema: $ref: '#/components/schemas/Season' /media: get: summary: Get media description: Returns all media (can be filtered and limited) in a JSON object. tags: - media parameters: - in: query name: take schema: type: number nullable: true example: 20 - in: query name: skip schema: type: number nullable: true example: 0 - in: query name: filter schema: type: string nullable: true enum: [ all, available, partial, allavailable, processing, pending, deleted, ] - in: query name: sort schema: type: string enum: [added, modified, mediaAdded] default: added responses: '200': description: Returned media content: application/json: schema: type: object properties: pageInfo: $ref: '#/components/schemas/PageInfo' results: type: array /media/{mediaId}: delete: summary: Delete media item description: Removes a media item. The `MANAGE_REQUESTS` permission is required to perform this action. tags: - media parameters: - in: path name: mediaId description: Media ID required: true example: '1' schema: type: string responses: '204': description: Succesfully removed media item /media/{mediaId}/{status}: post: summary: Update media status description: Updates a media item's status and returns the media in JSON format tags: - media parameters: - in: path name: mediaId description: Media ID required: true example: '1' schema: type: string - in: path name: status description: New status required: true example: available schema: type: string enum: [available, partial, processing, pending, unknown, deleted] requestBody: content: application/json: schema: type: object properties: is4k: type: boolean example: false responses: '200': description: Returned media /media/{mediaId}/watch_data: get: summary: Get watch data description: | Returns play count, play duration, and users who have watched the media. Requires the `ADMIN` permission. tags: - media parameters: - in: path name: mediaId description: Media ID required: true example: '1' schema: type: string responses: '200': description: Users content: application/json: schema: type: object properties: data: type: object properties: playCount7Days: type: number playCount30Days: type: number playCount: type: number users: type: array items: $ref: '#/components/schemas/User' data4k: type: object properties: playCount7Days: type: number playCount30Days: type: number playCount: type: number users: type: array items: $ref: '#/components/schemas/User' /service/radarr: get: summary: Get non-sensitive Radarr server list description: Returns a list of Radarr server IDs and names in a JSON object. tags: - service responses: '200': description: Request successful content: application/json: schema: type: array items: $ref: '#/components/schemas/RadarrSettings' /service/radarr/{radarrId}: get: summary: Get Radarr server quality profiles and root folders description: Returns a Radarr server's quality profile and root folder details in a JSON object. tags: - service parameters: - in: path name: radarrId required: true schema: type: number example: 0 responses: '200': description: Request successful content: application/json: schema: type: object properties: server: $ref: '#/components/schemas/RadarrSettings' profiles: $ref: '#/components/schemas/ServiceProfile' /service/sonarr: get: summary: Get non-sensitive Sonarr server list description: Returns a list of Sonarr server IDs and names in a JSON object. tags: - service responses: '200': description: Request successful content: application/json: schema: type: array items: $ref: '#/components/schemas/SonarrSettings' /service/sonarr/{sonarrId}: get: summary: Get Sonarr server quality profiles and root folders description: Returns a Sonarr server's quality profile and root folder details in a JSON object. tags: - service parameters: - in: path name: sonarrId required: true schema: type: number example: 0 responses: '200': description: Request successful content: application/json: schema: type: object properties: server: $ref: '#/components/schemas/SonarrSettings' profiles: $ref: '#/components/schemas/ServiceProfile' /service/sonarr/lookup/{tmdbId}: get: summary: Get series from Sonarr description: Returns a list of series returned by searching for the name in Sonarr. tags: - service parameters: - in: path name: tmdbId required: true schema: type: number example: 0 responses: '200': description: Request successful content: application/json: schema: type: array items: $ref: '#/components/schemas/SonarrSeries' /backdrops: get: summary: Get backdrops of trending items description: Returns a list of backdrop image paths in a JSON array. security: [] tags: - tmdb responses: '200': description: Results content: application/json: schema: type: array items: type: string /keyword/{keywordId}: get: summary: Get keyword description: | Returns a single keyword in JSON format. tags: - other parameters: - in: path name: keywordId required: true schema: type: number example: 1 responses: '200': description: Keyword returned content: application/json: schema: $ref: '#/components/schemas/Keyword' /missing-items: get: summary: Get missing item requests description: | Returns a paginated list of missing item requests with optional filtering. tags: - missing-items parameters: - in: query name: limit schema: type: integer default: 10 minimum: 1 maximum: 100 description: Number of items to return - in: query name: offset schema: type: integer default: 0 minimum: 0 description: Number of items to skip - in: query name: status schema: type: string enum: [pending, approved, declined, available, processing, failed, partially_available] description: Filter by request status - in: query name: mediaType schema: type: string enum: [movie, tv] description: Filter by media type - in: query name: collectionSource schema: type: string enum: [trakt, tmdb, imdb, letterboxd] description: Filter by collection source - in: query name: requestService schema: type: string enum: [overseerr, radarr, sonarr] description: Filter by request service responses: '200': description: Missing item requests returned content: application/json: schema: $ref: '#/components/schemas/MissingItemsResponse' /missing-items/recent: get: summary: Get recent missing item requests description: | Returns the most recent missing item requests for dashboard display. tags: - missing-items parameters: - in: query name: limit schema: type: integer default: 5 minimum: 1 maximum: 20 description: Number of recent items to return responses: '200': description: Recent missing item requests returned content: application/json: schema: type: object properties: results: type: array items: $ref: '#/components/schemas/MissingItemRequest' /missing-items/stats: get: summary: Get missing item statistics description: | Returns statistics about missing item requests including status breakdowns and source/service distribution. tags: - missing-items responses: '200': description: Missing item statistics returned content: application/json: schema: $ref: '#/components/schemas/MissingItemStats' /missing-items/sync: post: summary: Sync missing item status description: | Synchronizes the status of missing item requests with their current status in Overseerr. This endpoint checks recent missing item requests and updates their status based on the current state in Overseerr (e.g., pending → declined, approved → available). tags: - missing-items responses: '200': description: Missing item status sync completed successfully content: application/json: schema: type: object properties: message: type: string example: "Missing item status sync completed" '500': description: Failed to sync missing item status content: application/json: schema: type: object properties: message: type: string example: "Failed to sync missing item status" /dashboard/stats: get: summary: Get dashboard statistics description: | Returns general dashboard statistics including collection counts, activity stats from Tautulli, and weekly plays. tags: - dashboard responses: '200': description: Dashboard statistics returned successfully content: application/json: schema: $ref: '#/components/schemas/DashboardStats' '500': description: Failed to get dashboard statistics content: application/json: schema: type: object properties: error: type: string example: "Failed to get dashboard stats" message: type: string example: "Tautulli connection failed" /dashboard/collections: get: summary: Get collection statistics description: | Returns detailed collection statistics from Tautulli including play counts, duration, and user activity. tags: - dashboard parameters: - in: query name: limit schema: type: integer default: 10 minimum: 1 maximum: 50 description: Number of collections to return - in: query name: statType schema: type: string enum: [plays, duration] default: plays description: Type of statistics to sort by - in: query name: days schema: type: integer default: 30 minimum: 1 maximum: 365 description: Number of days to include in statistics responses: '200': description: Collection statistics returned successfully content: application/json: schema: $ref: '#/components/schemas/CollectionStatsResponse' '400': description: Tautulli not configured content: application/json: schema: type: object properties: error: type: string example: "Tautulli not configured" message: type: string example: "Tautulli settings are required to fetch collection statistics" '500': description: Failed to get collection statistics content: application/json: schema: type: object properties: error: type: string example: "Failed to get collection statistics" message: type: string example: "Tautulli API error" /dashboard/activity: get: summary: Get activity statistics description: | Returns recent activity and general statistics from Tautulli including top movies and TV shows. tags: - dashboard parameters: - in: query name: days schema: type: integer default: 7 minimum: 1 maximum: 365 description: Number of days to include in activity statistics - in: query name: limit schema: type: integer default: 10 minimum: 1 maximum: 50 description: Number of items to return responses: '200': description: Activity statistics returned successfully content: application/json: schema: $ref: '#/components/schemas/ActivityStatsResponse' '400': description: Tautulli not configured content: application/json: schema: type: object properties: error: type: string example: "Tautulli not configured" message: type: string example: "Tautulli settings are required to fetch activity statistics" '500': description: Failed to get activity statistics content: application/json: schema: type: object properties: error: type: string example: "Failed to get activity statistics" message: type: string example: "Tautulli API error" # Hub Management Endpoints /hubs/configs: get: summary: Get current Plex hub configurations tags: - settings responses: '200': description: Hub configurations returned successfully content: application/json: schema: type: object properties: hubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' preExistingCollectionConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' '500': description: Failed to get hub configurations post: summary: Save Plex hub configurations tags: - settings requestBody: required: true content: application/json: schema: type: object properties: hubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' preExistingCollectionConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' responses: '200': description: Configurations saved successfully '400': description: Invalid input '500': description: Failed to save configurations /hubs/configs/append: post: summary: Append new hub configurations to existing ones tags: - settings requestBody: required: true content: application/json: schema: type: object properties: hubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' preExistingCollectionConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' responses: '200': description: Configurations appended successfully '400': description: Invalid input '500': description: Failed to append configurations /hubs/libraries/{sectionId}/move: put: summary: Move a hub to a new position in the library tags: - settings parameters: - in: path name: sectionId required: true schema: type: string description: Plex library section ID requestBody: required: true content: application/json: schema: type: object properties: hubId: type: string description: Hub identifier to move afterHubId: type: string description: Hub identifier to move after (optional) required: - hubId responses: '200': description: Hub moved successfully '400': description: Invalid input '500': description: Failed to move hub /hubs/libraries/{sectionId}/reorder: put: summary: Reorder multiple hubs in a library section tags: - settings parameters: - in: path name: sectionId required: true schema: type: string description: Plex library section ID requestBody: required: true content: application/json: schema: type: object properties: hubOrder: type: array items: type: string description: Array of hub IDs in desired order required: - hubOrder responses: '200': description: Hubs reordered successfully '400': description: Invalid input '500': description: Failed to reorder hubs /hubs/libraries/{sectionId}/visibility: put: summary: Update hub visibility settings tags: - settings parameters: - in: path name: sectionId required: true schema: type: string description: Plex library section ID requestBody: required: true content: application/json: schema: type: object properties: hubId: type: string description: Hub identifier visibility: type: object description: Visibility configuration required: - hubId - visibility responses: '200': description: Hub visibility updated successfully '400': description: Invalid input '500': description: Failed to update hub visibility # Discovery Endpoints /discovery/hubs/libraries: get: summary: Get all library hubs across all sections tags: - search responses: '200': description: Library hubs returned successfully '500': description: Failed to fetch library hubs /discovery/hubs/libraries/{sectionId}: get: summary: Get hubs for a specific library section tags: - search parameters: - in: path name: sectionId required: true schema: type: string description: Plex library section ID responses: '200': description: Library hubs returned successfully '500': description: Failed to fetch library hubs /discovery/hubs/libraries/{sectionId}/manage: get: summary: Get hub management interface for a library section tags: - search parameters: - in: path name: sectionId required: true schema: type: string description: Plex library section ID responses: '200': description: Hub management data returned successfully '500': description: Failed to fetch hub management data /discovery/hubs/scan: get: summary: Discover available Plex hubs and convert them to hub configurations tags: - search responses: '200': description: Hub discovery completed successfully content: application/json: schema: type: object properties: success: type: boolean discoveredHubConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' discoveredPreExistingConfigs: type: array items: $ref: '#/components/schemas/PlexHubConfig' totalHubsFound: type: integer totalPreExistingCollectionsFound: type: integer totalActualCollections: type: integer '500': description: Failed to discover Plex hubs /discovery/hubs/status: get: summary: Get hub management system status and capabilities tags: - search responses: '200': description: Hub management status returned successfully content: application/json: schema: type: object properties: enabled: type: boolean plexConnected: type: boolean libraryCount: type: integer capabilities: type: object properties: hubReordering: type: boolean visibilityControl: type: boolean builtInHubManagement: type: boolean collectionHubManagement: type: boolean '500': description: Failed to get hub management status # Poster Management Endpoints /posters/templates: get: summary: Get all poster templates description: Retrieves all active poster templates with their metadata and configuration. tags: - posters responses: '200': description: Templates retrieved successfully content: application/json: schema: type: object properties: templates: type: array items: type: object properties: id: type: integer example: 1 name: type: string example: "Classic Movie Poster" description: type: string example: "A classic movie poster template with text overlays" isDefault: type: boolean example: false templateData: type: object properties: width: type: number example: 500 height: type: number example: 750 background: type: object properties: type: type: string enum: [color, gradient] example: "gradient" color: type: string example: "#6366f1" secondaryColor: type: string example: "#1e1b4b" textElements: type: array items: type: object properties: id: type: string example: "text-1" type: type: string example: "collection-title" text: type: string example: "Collection Name" x: type: number example: 50 y: type: number example: 100 width: type: number example: 400 height: type: number example: 60 fontSize: type: number example: 32 fontFamily: type: string example: "Arial, sans-serif" fontWeight: type: string enum: [normal, bold] example: "bold" fontStyle: type: string enum: [normal, italic] example: "normal" color: type: string example: "#ffffff" textAlign: type: string enum: [left, center, right] example: "center" maxLines: type: number example: 2 iconElements: type: array items: type: object properties: id: type: string example: "icon-1" type: type: string enum: [source-logo, custom-icon] example: "source-logo" iconPath: type: string example: "/icons/tmdb-logo.svg" x: type: number example: 25 y: type: number example: 25 width: type: number example: 50 height: type: number example: 50 grayscale: type: boolean example: false contentGrid: type: object properties: id: type: string example: "grid-1" x: type: number example: 50 y: type: number example: 200 width: type: number example: 400 height: type: number example: 400 columns: type: number example: 3 rows: type: number example: 3 spacing: type: number example: 10 cornerRadius: type: number example: 8 createdAt: type: string format: date-time updatedAt: type: string format: date-time '500': description: Failed to fetch poster templates post: summary: Create new poster template description: Creates a new poster template with the provided configuration. tags: - posters requestBody: required: true content: application/json: schema: type: object required: - name - templateData properties: name: type: string example: "My Custom Template" description: type: string example: "A custom poster template" templateData: type: object description: "Template configuration data (same structure as in GET response)" responses: '201': description: Template created successfully content: application/json: schema: type: object description: "Same structure as template object in GET response" '400': description: Invalid template data or validation failed '401': description: Authentication required '500': description: Failed to create poster template /posters/templates/{id}: put: summary: Update poster template description: Updates an existing poster template. Users can only update their own templates. tags: - posters parameters: - name: id in: path required: true schema: type: integer description: Template ID requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: type: string templateData: type: object responses: '200': description: Template updated successfully '400': description: Invalid template data '401': description: Authentication required '403': description: Permission denied '404': description: Template not found '500': description: Failed to update poster template delete: summary: Delete poster template description: Soft deletes a poster template. Users can only delete their own templates. Default templates cannot be deleted. tags: - posters parameters: - name: id in: path required: true schema: type: integer description: Template ID responses: '204': description: Template deleted successfully '401': description: Authentication required '403': description: Permission denied or cannot delete default template '404': description: Template not found '500': description: Failed to delete poster template /posters/templates/{id}/set-default: post: summary: Set template as default description: Sets the specified template as the default template for poster generation. Only one template can be default at a time. tags: - posters parameters: - name: id in: path required: true schema: type: integer description: Template ID example: 1 responses: '200': description: Template set as default successfully content: application/json: schema: type: object properties: message: type: string example: "Template set as default successfully" template: type: object properties: id: type: integer example: 1 name: type: string example: "My Custom Template" isDefault: type: boolean example: true '401': description: Authentication required '404': description: Template not found '500': description: Failed to set template as default /posters/templates/{id}/preview: get: summary: Generate template preview description: Generates a preview image of a poster template with optional collection data. tags: - posters parameters: - name: id in: path required: true schema: type: integer description: Template ID - name: collectionName in: query schema: type: string default: "Preview Collection" description: Collection name to use in preview - name: collectionType in: query schema: type: string description: Collection type - name: mediaType in: query schema: type: string enum: [movie, tv] default: movie description: Media type responses: '200': description: Preview image generated successfully content: image/png: schema: type: string format: binary '400': description: Invalid template ID or failed to generate preview '500': description: Failed to generate template preview /posters/templates/validate: post: summary: Validate template data description: Validates poster template data structure and returns sanitized data if valid. tags: - posters requestBody: required: true content: application/json: schema: type: object required: - templateData properties: templateData: type: object description: "Template data to validate" responses: '200': description: Validation completed content: application/json: schema: type: object properties: isValid: type: boolean example: true errors: type: array items: type: string example: [] warnings: type: array items: type: string example: [] sanitizedData: type: object description: "Sanitized template data (only present if isValid is true)" '400': description: Template data is required '500': description: Failed to validate template data /posters/saved: get: summary: Get saved posters description: Retrieves all saved posters for the authenticated user. tags: - posters responses: '200': description: Saved posters retrieved successfully content: application/json: schema: type: object properties: posters: type: array items: type: object properties: id: type: integer example: 1 name: type: string example: "My Custom Poster" description: type: string example: "A custom poster for my collection" filename: type: string example: "poster-abc123.jpg" thumbnailFilename: type: string example: "thumb-abc123.jpg" posterData: type: object description: "Poster configuration data" createdAt: type: string format: date-time updatedAt: type: string format: date-time '401': description: Authentication required '500': description: Failed to fetch saved posters post: summary: Create saved poster description: Creates a new saved poster with the provided data. tags: - posters requestBody: required: true content: application/json: schema: type: object required: - name - posterData properties: name: type: string example: "My Saved Poster" description: type: string example: "Description of my poster" posterData: type: object description: "Poster configuration data" filename: type: string example: "poster-abc123.jpg" thumbnailFilename: type: string example: "thumb-abc123.jpg" responses: '201': description: Saved poster created successfully '400': description: Poster name and data are required '401': description: Authentication required '500': description: Failed to create saved poster /posters/saved/{id}: put: summary: Update saved poster description: Updates an existing saved poster. Users can only update their own posters. tags: - posters parameters: - name: id in: path required: true schema: type: integer description: Poster ID requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: type: string posterData: type: object filename: type: string thumbnailFilename: type: string responses: '200': description: Saved poster updated successfully '401': description: Authentication required '404': description: Poster not found '500': description: Failed to update saved poster delete: summary: Delete saved poster description: Deletes a saved poster. Supports both database posters (integer ID) and file-based posters (string ID with 'file-' prefix). Users can only delete their own posters. tags: - posters parameters: - name: id in: path required: true schema: oneOf: - type: integer description: Database poster ID - type: string pattern: '^file-.+$' description: File-based poster ID (starts with 'file-') description: Poster ID (integer for database posters, or string starting with 'file-' for file-based posters) responses: '204': description: Saved poster deleted successfully '401': description: Authentication required '404': description: Poster not found '409': description: Poster is currently in use by collections content: application/json: schema: type: object properties: error: type: string example: "Poster is currently in use" message: type: string example: "This poster is being used by the following collections: Movie Collection, TV Collection" collections: type: array items: type: string example: ["Movie Collection", "TV Collection"] '500': description: Failed to delete saved poster /posters/icons/upload: post: summary: Upload icon/asset description: Uploads an icon or asset file for use in poster templates. tags: - posters requestBody: required: true content: multipart/form-data: schema: type: object required: - icon properties: icon: type: string format: binary description: "Icon file (JPEG, PNG, WebP, SVG, max 10MB)" name: type: string description: "Name for the icon" category: type: string description: "Category for the icon" tags: type: string description: "Comma-separated tags" description: type: string description: "Description of the icon" responses: '201': description: Icon uploaded successfully content: application/json: schema: type: object properties: icon: type: object properties: id: type: string name: type: string filename: type: string category: type: string tags: type: array items: type: string description: type: string '400': description: No file uploaded or invalid file type '500': description: Failed to upload icon /posters/icons/download: post: summary: Download icon from URL description: Downloads an icon from a URL and saves it to the system. tags: - posters requestBody: required: true content: application/json: schema: type: object required: - url properties: url: type: string format: uri example: "https://example.com/icon.png" name: type: string category: type: string tags: type: array items: type: string description: type: string responses: '201': description: Icon downloaded successfully '400': description: URL is required or download failed '500': description: Failed to download icon /posters/icons: get: summary: List available icons description: Retrieves a list of available icons with optional filtering. tags: - posters parameters: - name: type in: query schema: type: string enum: [user, system] description: Filter by icon type - name: category in: query schema: type: string description: Filter by category - name: tags in: query schema: type: array items: type: string description: Filter by tags - name: search in: query schema: type: string description: Search in name and description responses: '200': description: Icons retrieved successfully content: application/json: schema: type: object properties: icons: type: array items: type: object properties: id: type: string name: type: string filename: type: string category: type: string tags: type: array items: type: string description: type: string type: type: string enum: [user, system] '500': description: Failed to list icons /posters/icons/categories: get: summary: List icon categories description: Retrieves all available icon categories with statistics. tags: - posters responses: '200': description: Categories retrieved successfully content: application/json: schema: type: object properties: categories: type: array items: type: object properties: name: type: string displayName: type: string description: type: string iconCount: type: integer '500': description: Failed to list icon categories /posters/icons/{id}: delete: summary: Delete icon description: Deletes an icon by ID. tags: - posters parameters: - name: id in: path required: true schema: type: string description: Icon ID responses: '204': description: Icon deleted successfully '400': description: Icon ID is required '404': description: Icon not found '500': description: Failed to delete icon /posters/files/{filename}: get: summary: Serve poster files description: Serves poster image files by filename. tags: - posters parameters: - name: filename in: path required: true schema: type: string description: Poster filename responses: '200': description: Poster image served successfully content: image/jpeg: schema: type: string format: binary '400': description: Filename is required '404': description: Poster file not found '500': description: Failed to serve poster file /posters/thumbnails/{filename}: get: summary: Serve thumbnail files description: Serves poster thumbnail files by filename. tags: - posters parameters: - name: filename in: path required: true schema: type: string description: Thumbnail filename responses: '200': description: Thumbnail image served successfully content: image/jpeg: schema: type: string format: binary '400': description: Filename is required '404': description: Thumbnail not found '500': description: Failed to serve thumbnail file /posters/icons/{type}/{filename}: get: summary: Serve icon files description: Serves icon files by type and filename. tags: - posters parameters: - name: type in: path required: true schema: type: string enum: [user, system] description: Icon type - name: filename in: path required: true schema: type: string description: Icon filename responses: '200': description: Icon file served successfully content: image/*: schema: type: string format: binary '400': description: Invalid icon type or filename required '404': description: Icon file not found '500': description: Failed to serve icon file /source-colors: get: summary: Get all source colors description: Retrieves all source color mappings (database + defaults) for poster generation. tags: - source-colors responses: '200': description: Source colors retrieved successfully content: application/json: schema: type: object additionalProperties: type: object properties: primaryColor: type: string example: "#01b4e4" secondaryColor: type: string example: "#0d253f" textColor: type: string example: "#ffffff" example: tmdb: primaryColor: "#01b4e4" secondaryColor: "#0d253f" textColor: "#ffffff" trakt: primaryColor: "#ed2224" secondaryColor: "#1f1a1a" textColor: "#ffffff" '500': description: Failed to get source colors /source-colors/{sourceType}: get: summary: Get specific source color scheme description: Retrieves color scheme for a specific source type. tags: - source-colors parameters: - name: sourceType in: path required: true schema: type: string example: tmdb responses: '200': description: Source color scheme retrieved successfully content: application/json: schema: type: object properties: primaryColor: type: string example: "#01b4e4" secondaryColor: type: string example: "#0d253f" textColor: type: string example: "#ffffff" '500': description: Failed to get source colors put: summary: Update source colors description: Updates color scheme for a specific source type. This affects all templates using source colors. tags: - source-colors parameters: - name: sourceType in: path required: true schema: type: string example: tmdb requestBody: required: true content: application/json: schema: type: object required: - primaryColor - secondaryColor - textColor properties: primaryColor: type: string pattern: '^#[0-9a-fA-F]{6}$' example: "#01b4e4" secondaryColor: type: string pattern: '^#[0-9a-fA-F]{6}$' example: "#0d253f" textColor: type: string pattern: '^#[0-9a-fA-F]{6}$' example: "#ffffff" responses: '200': description: Source colors updated successfully content: application/json: schema: type: object properties: message: type: string example: "Source colors updated for tmdb" colors: type: object properties: primaryColor: type: string example: "#01b4e4" secondaryColor: type: string example: "#0d253f" textColor: type: string example: "#ffffff" '400': description: Invalid input - missing fields or invalid hex colors '500': description: Failed to update source colors delete: summary: Reset source colors to defaults description: Resets the specified source type colors to system defaults. tags: - source-colors parameters: - name: sourceType in: path required: true schema: type: string example: tmdb responses: '200': description: Source colors reset successfully content: application/json: schema: type: object properties: message: type: string example: "Source colors reset to defaults for tmdb" colors: type: object properties: primaryColor: type: string example: "#01b4e4" secondaryColor: type: string example: "#0d253f" textColor: type: string example: "#ffffff" '500': description: Failed to reset source colors /source-colors/reset: post: summary: Reset all source colors to defaults description: Resets all source color customizations back to system defaults. tags: - source-colors responses: '200': description: All source colors reset successfully content: application/json: schema: type: object properties: message: type: string example: "All source colors reset to defaults" colors: type: object additionalProperties: type: object properties: primaryColor: type: string secondaryColor: type: string textColor: type: string '500': description: Failed to reset source colors security: - cookieAuth: [] - apiKey: []