{ "components": { "schemas": { "AccountPrivacyOut": { "additionalProperties": true, "description": "One Plex account's share filter, as plex.tv reports it RIGHT NOW.", "properties": { "account_id": { "title": "Account Id", "type": "integer" }, "display_name": { "title": "Display Name", "type": "string" }, "hides": { "items": { "type": "string" }, "title": "Hides", "type": "array" }, "manage_sharing": { "title": "Manage Sharing", "type": "boolean" }, "missing": { "items": { "type": "string" }, "title": "Missing", "type": "array" }, "other_conditions": { "items": { "type": "string" }, "title": "Other Conditions", "type": "array" }, "restriction_profile": { "title": "Restriction Profile", "type": "string" }, "should_hide": { "items": { "type": "string" }, "title": "Should Hide", "type": "array" }, "slug": { "title": "Slug", "type": "string" }, "state": { "title": "State", "type": "string" }, "user": { "title": "User", "type": "string" }, "user_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "User Id" }, "user_type": { "title": "User Type", "type": "string" } }, "required": [ "user", "display_name", "slug", "account_id", "user_id", "user_type", "restriction_profile", "manage_sharing", "state", "hides", "should_hide", "missing", "other_conditions" ], "title": "AccountPrivacyOut", "type": "object" }, "AcquisitionReleaseIn": { "additionalProperties": false, "description": "One owner-confirmed decision on an exact terminal acquisition record.", "properties": { "checked_destination": { "const": true, "title": "Checked Destination", "type": "boolean" }, "expected_status": { "enum": [ "outcome_unknown", "succeeded" ], "title": "Expected Status", "type": "string" }, "review_token": { "maxLength": 64, "minLength": 64, "pattern": "^[a-f0-9]{64}$", "title": "Review Token", "type": "string" } }, "required": [ "review_token", "expected_status", "checked_destination" ], "title": "AcquisitionReleaseIn", "type": "object" }, "AiInstructionsIn": { "additionalProperties": false, "description": "What AI web search should look for on this row (#138). ``default`` uses the built-in wording\nplus the server-wide instructions; ``add`` appends ``text`` to them; ``own`` replaces them.", "properties": { "mode": { "default": "default", "description": "AI instructions must be default, add or own", "enum": [ "add", "default", "own" ], "title": "Mode", "type": "string" }, "text": { "default": "", "maxLength": 2000, "title": "Text", "type": "string" } }, "title": "AiInstructionsIn", "type": "object" }, "AiInstructionsOut": { "additionalProperties": true, "description": "A row's AI web search instructions as the editor reads them.", "properties": { "mode": { "description": "default, add or own", "enum": [ "add", "default", "own" ], "title": "Mode", "type": "string" }, "text": { "title": "Text", "type": "string" } }, "required": [ "mode", "text" ], "title": "AiInstructionsOut", "type": "object" }, "AiPauseRequest": { "additionalProperties": false, "properties": { "paused": { "title": "Paused", "type": "boolean" } }, "required": [ "paused" ], "title": "AiPauseRequest", "type": "object" }, "ApiTokenCreatedOut": { "additionalProperties": true, "properties": { "created_at": { "title": "Created At", "type": "string" }, "token": { "title": "Token", "type": "string" } }, "required": [ "token", "created_at" ], "title": "ApiTokenCreatedOut", "type": "object" }, "ApiTokenRevokedOut": { "additionalProperties": true, "properties": { "enabled": { "title": "Enabled", "type": "boolean" } }, "required": [ "enabled" ], "title": "ApiTokenRevokedOut", "type": "object" }, "ApiTokenStatusOut": { "additionalProperties": true, "description": "`GET /api-token`. No example or default is declared on `token` \u2014 a schema is documentation\nthat ships to the browser, and a credential has no business in one (plex-safety rule 9).", "properties": { "created_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Created At" }, "enabled": { "title": "Enabled", "type": "boolean" }, "token": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Token" } }, "required": [ "enabled", "created_at", "token" ], "title": "ApiTokenStatusOut", "type": "object" }, "ArrOptionsOut": { "additionalProperties": true, "properties": { "quality_profiles": { "items": { "$ref": "#/components/schemas/QualityProfileOut" }, "title": "Quality Profiles", "type": "array" }, "root_folders": { "items": { "$ref": "#/components/schemas/RootFolderOut" }, "title": "Root Folders", "type": "array" } }, "required": [ "quality_profiles", "root_folders" ], "title": "ArrOptionsOut", "type": "object" }, "ArrStatusOut": { "additionalProperties": true, "description": "Per-row download status, plus whether each app actually answered.\n\n``reach`` is the half this used to omit. A failed Arr lookup is swallowed on purpose (one app\nbeing down must not blank the other), so an unreachable Radarr produced an all-``null`` map \u2014\nbyte-identical to \"Radarr is fine and tracks none of these\". The inbox therefore showed no\nbadges, for ever, with nothing anywhere saying why.", "properties": { "overseerr": { "default": "off", "enum": [ "ok", "unreachable", "off" ], "title": "Overseerr", "type": "string" }, "radarr": { "enum": [ "ok", "unreachable", "off" ], "title": "Radarr", "type": "string" }, "sonarr": { "enum": [ "ok", "unreachable", "off" ], "title": "Sonarr", "type": "string" }, "statuses": { "additionalProperties": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "title": "Statuses", "type": "object" } }, "required": [ "statuses", "radarr", "sonarr" ], "title": "ArrStatusOut", "type": "object" }, "BackupCreatedOut": { "additionalProperties": true, "description": "A manual backup carries no `created_at`: it was just taken, which is the answer.", "properties": { "name": { "title": "Name", "type": "string" }, "size_bytes": { "title": "Size Bytes", "type": "integer" } }, "required": [ "name", "size_bytes" ], "title": "BackupCreatedOut", "type": "object" }, "BackupOut": { "additionalProperties": true, "properties": { "created_at": { "title": "Created At", "type": "string" }, "name": { "title": "Name", "type": "string" }, "size_bytes": { "title": "Size Bytes", "type": "integer" } }, "required": [ "name", "size_bytes", "created_at" ], "title": "BackupOut", "type": "object" }, "BackupRestoredOut": { "additionalProperties": true, "properties": { "message": { "title": "Message", "type": "string" }, "privacy_note": { "title": "Privacy Note", "type": "string" }, "restored": { "title": "Restored", "type": "string" } }, "required": [ "restored", "message", "privacy_note" ], "title": "BackupRestoredOut", "type": "object" }, "BlockSeedBody": { "properties": { "media_type": { "default": "", "title": "Media Type", "type": "string" }, "title": { "default": "", "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "tmdb_id" ], "title": "BlockSeedBody", "type": "object" }, "BlockedSeedOut": { "additionalProperties": true, "description": "One blocked seed, always as a RECORD.\n\nThe bare-int list an old install stores is normalised by `blocked_entries` before it reaches here,\nso this shape is what both endpoints return whichever way the prefs are stored \u2014 see\n`shortlist/server/prefs.py`.", "properties": { "media_type": { "title": "Media Type", "type": "string" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "tmdb_id", "title", "media_type", "year" ], "title": "BlockedSeedOut", "type": "object" }, "BlockedSeedsOut": { "additionalProperties": true, "properties": { "blocked_seeds": { "items": { "$ref": "#/components/schemas/BlockedSeedOut" }, "title": "Blocked Seeds", "type": "array" } }, "required": [ "blocked_seeds" ], "title": "BlockedSeedsOut", "type": "object" }, "Body_upload_poster_image_api_collections__collection_id__poster_upload_post": { "properties": { "file": { "contentMediaType": "application/octet-stream", "title": "File", "type": "string" } }, "required": [ "file" ], "title": "Body_upload_poster_image_api_collections__collection_id__poster_upload_post", "type": "object" }, "BulkEnabled": { "properties": { "enabled": { "title": "Enabled", "type": "boolean" } }, "required": [ "enabled" ], "title": "BulkEnabled", "type": "object" }, "BulkEnabledOut": { "additionalProperties": true, "description": "What `POST /users/set-enabled` did: how many were touched, and how many had rows removed.", "properties": { "cleaned": { "title": "Cleaned", "type": "integer" }, "enabled": { "title": "Enabled", "type": "boolean" }, "updated": { "title": "Updated", "type": "integer" } }, "required": [ "updated", "cleaned", "enabled" ], "title": "BulkEnabledOut", "type": "object" }, "CapabilitiesOut": { "additionalProperties": true, "properties": { "ai": { "title": "Ai", "type": "boolean" } }, "required": [ "ai" ], "title": "CapabilitiesOut", "type": "object" }, "Capability": { "description": "One independently enforceable assistant permission.", "enum": [ "instance.read", "config.read", "catalog.read", "people.read", "activity.read", "changes.prepare", "history.use", "history.providers", "history.export", "rows.create", "rows.update", "rows.activate", "rows.delete", "audiences.write", "themes.write", "seasons.write", "config.write", "people.write", "schedules.write", "connections.manage", "runs.preview", "runs.execute", "jobs.cancel", "ai.generate", "requests.read", "requests.manage", "requests.send", "maintenance.execute", "secrets.read", "grants.manage" ], "title": "Capability", "type": "string" }, "CleanupOut": { "additionalProperties": true, "description": "What `POST /collections/{id}/cleanup` removed (or would remove, on a dry run).", "properties": { "dry_run": { "title": "Dry Run", "type": "boolean" }, "message": { "title": "Message", "type": "string" }, "removed": { "items": { "type": "string" }, "title": "Removed", "type": "array" } }, "required": [ "removed", "dry_run", "message" ], "title": "CleanupOut", "type": "object" }, "CleanupRequest": { "properties": { "dry_run": { "default": false, "title": "Dry Run", "type": "boolean" } }, "title": "CleanupRequest", "type": "object" }, "ClearedDeletedRowsOut": { "additionalProperties": true, "properties": { "cleared": { "title": "Cleared", "type": "integer" }, "picks": { "title": "Picks", "type": "integer" }, "slugs": { "items": { "type": "string" }, "title": "Slugs", "type": "array" } }, "required": [ "cleared", "picks", "slugs" ], "title": "ClearedDeletedRowsOut", "type": "object" }, "ClearedOut": { "additionalProperties": true, "properties": { "cleared": { "title": "Cleared", "type": "integer" } }, "required": [ "cleared" ], "title": "ClearedOut", "type": "object" }, "CollectionCountOut": { "additionalProperties": true, "properties": { "found": { "title": "Found", "type": "boolean" }, "in_library": { "title": "In Library", "type": "integer" }, "section_key": { "title": "Section Key", "type": "string" }, "title": { "title": "Title", "type": "string" } }, "required": [ "title", "section_key", "found", "in_library" ], "title": "CollectionCountOut", "type": "object" }, "CollectionIO": { "additionalProperties": true, "description": "A Plex collection, by library and title \u2014 never ratingKey: Kometa recreates its seasonal ones each year.", "properties": { "section_key": { "title": "Section Key", "type": "string" }, "section_title": { "title": "Section Title", "type": "string" }, "title": { "title": "Title", "type": "string" } }, "required": [ "section_key", "section_title", "title" ], "title": "CollectionIO", "type": "object" }, "CollectionIn": { "additionalProperties": false, "properties": { "ai_instructions": { "$ref": "#/components/schemas/AiInstructionsIn" }, "audience": { "default": "everyone", "description": "Everyone, or the subset named by audience_user_ids.", "enum": [ "everyone", "subset" ], "title": "Audience", "type": "string" }, "audience_user_ids": { "items": { "type": "integer" }, "title": "Audience User Ids", "type": "array" }, "avoid_rows": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "description": "Slugs of other per-person rows whose titles this row keeps out; null is none.", "title": "Avoid Rows" }, "build": { "default": "per_person", "description": "Who the row is built for: one per person, or one shared row.", "enum": [ "per_person", "shared" ], "title": "Build", "type": "string" }, "candidate_sources": { "items": { "type": "string" }, "title": "Candidate Sources", "type": "array" }, "cold_start": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "What this row does for someone with too little watch history; null inherits the global setting.", "enum": [ "popular", "skip", null ], "title": "Cold Start" }, "defer_rename": { "default": false, "title": "Defer Rename", "type": "boolean" }, "description": { "default": "", "description": "The collection's Plex summary; takes {user}, {library_name} and {top_seed}. Empty leaves the summary on Plex alone.", "maxLength": 2000, "title": "Description", "type": "string" }, "dry_run": { "default": false, "title": "Dry Run", "type": "boolean" }, "enabled": { "default": true, "title": "Enabled", "type": "boolean" }, "explore_brief": { "default": "", "description": "What kind of themes Explore should look for; blank lets the AI choose.", "maxLength": 500, "title": "Explore Brief", "type": "string" }, "fallback_name": { "default": "", "maxLength": 255, "title": "Fallback Name", "type": "string" }, "favourite_count": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Favourite Count" }, "hub_anchor": { "additionalProperties": { "$ref": "#/components/schemas/HubAnchorIn" }, "title": "Hub Anchor", "type": "object" }, "idle_hold_days": { "anyOf": [ { "maximum": 365.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Idle Hold Days" }, "library_keys": { "items": { "type": "string" }, "title": "Library Keys", "type": "array" }, "max_runtime": { "anyOf": [ { "maximum": 600.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Max Runtime" }, "max_seeds": { "anyOf": [ { "maximum": 100.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Max Seeds" }, "max_year": { "anyOf": [ { "maximum": 2100.0, "minimum": 1870.0, "type": "integer" }, { "type": "null" } ], "title": "Max Year" }, "media": { "default": "both", "description": "Which library types this row builds in.", "enum": [ "both", "movie", "show" ], "title": "Media", "type": "string" }, "min_rating": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "number" }, { "type": "null" } ], "title": "Min Rating" }, "min_watchers": { "default": 2, "minimum": 2.0, "title": "Min Watchers", "type": "integer" }, "min_year": { "anyOf": [ { "maximum": 2100.0, "minimum": 1870.0, "type": "integer" }, { "type": "null" } ], "title": "Min Year" }, "name": { "maxLength": 255, "minLength": 1, "title": "Name", "type": "string" }, "name_template": { "default": "", "title": "Name Template", "type": "string" }, "older_count": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Older Count" }, "pick_order": { "default": "best", "description": "How the delivered collection is ordered.", "enum": [ "best", "new_first", "newest", "rating", "rotate", "shuffle" ], "title": "Pick Order", "type": "string" }, "pin_top": { "default": false, "title": "Pin Top", "type": "boolean" }, "placement": { "default": "both", "description": "Where the OWNER's own collection appears.", "enum": [ "both", "home", "library", "off" ], "title": "Placement", "type": "string" }, "placement_friends": { "default": "both", "description": "Where each FRIEND's own collection appears.", "enum": [ "both", "home", "library", "off" ], "title": "Placement Friends", "type": "string" }, "poster": { "$ref": "#/components/schemas/PosterIn" }, "recency": { "anyOf": [ { "maximum": 1.0, "minimum": -1.0, "type": "number" }, { "type": "null" } ], "title": "Recency" }, "recent_count": { "anyOf": [ { "maximum": 25.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Recent Count" }, "refresh_days": { "anyOf": [ { "maximum": 365.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Refresh Days" }, "refresh_share": { "anyOf": [ { "exclusiveMinimum": 0.0, "maximum": 1.0, "type": "number" }, { "type": "null" } ], "description": "The share of picks swapped on a refresh night; null keeps two thirds.", "title": "Refresh Share" }, "repeat_cooldown_days": { "anyOf": [ { "maximum": 365.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "description": "Don't pick a title again within this many days; null is off.", "title": "Repeat Cooldown Days" }, "req_auto_min_demand": { "anyOf": [ { "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Req Auto Min Demand" }, "req_auto_min_rating": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "number" }, { "type": "null" } ], "title": "Req Auto Min Rating" }, "req_auto_send": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Req Auto Send" }, "req_auto_user_tag": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Req Auto User Tag" }, "req_language_mode": { "anyOf": [ { "maxLength": 16, "type": "string" }, { "type": "null" } ], "enum": [ "any", "prefer", "only", null ], "title": "Req Language Mode" }, "req_max_per_row": { "anyOf": [ { "maximum": 100.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Req Max Per Row" }, "req_max_year": { "anyOf": [ { "maximum": 2999.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Req Max Year" }, "req_min_demand": { "anyOf": [ { "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Req Min Demand" }, "req_min_rating": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "number" }, { "type": "null" } ], "title": "Req Min Rating" }, "req_min_rating_other": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "number" }, { "type": "null" } ], "title": "Req Min Rating Other" }, "req_min_votes": { "anyOf": [ { "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Req Min Votes" }, "req_min_year": { "anyOf": [ { "maximum": 2999.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Req Min Year" }, "req_preferred_languages": { "anyOf": [ { "items": { "type": "string" }, "maxItems": 50, "type": "array" }, { "type": "null" } ], "title": "Req Preferred Languages" }, "req_radarr_quality_profile_id": { "anyOf": [ { "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Req Radarr Quality Profile Id" }, "req_radarr_root_folder": { "anyOf": [ { "maxLength": 512, "type": "string" }, { "type": "null" } ], "title": "Req Radarr Root Folder" }, "req_season_count": { "anyOf": [ { "maximum": 99.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Req Season Count" }, "req_skip_library_keys": { "anyOf": [ { "items": { "type": "string" }, "maxItems": 100, "type": "array" }, { "type": "null" } ], "title": "Req Skip Library Keys" }, "req_sonarr_monitor": { "anyOf": [ { "maxLength": 32, "type": "string" }, { "type": "null" } ], "enum": [ "all", "firstSeason", "lastSeason", "pilot", "none", null ], "title": "Req Sonarr Monitor" }, "req_sonarr_quality_profile_id": { "anyOf": [ { "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Req Sonarr Quality Profile Id" }, "req_sonarr_root_folder": { "anyOf": [ { "maxLength": 512, "type": "string" }, { "type": "null" } ], "title": "Req Sonarr Root Folder" }, "request_tag": { "default": "", "maxLength": 64, "title": "Request Tag", "type": "string" }, "requests_row": { "default": false, "title": "Requests Row", "type": "boolean" }, "requests_tag_pattern": { "default": "", "maxLength": 128, "title": "Requests Tag Pattern", "type": "string" }, "requests_window_days": { "default": 90, "maximum": 3650.0, "minimum": 0.0, "title": "Requests Window Days", "type": "integer" }, "rewatch": { "default": false, "title": "Rewatch", "type": "boolean" }, "rewatch_cooldown_days": { "default": 30, "maximum": 365.0, "minimum": 0.0, "title": "Rewatch Cooldown Days", "type": "integer" }, "schedule": { "default": "30 3 * * *", "maxLength": 64, "title": "Schedule", "type": "string" }, "season_after_days": { "default": 0, "description": "How many days after each season's day the row stays up.", "maximum": 30.0, "minimum": 0.0, "title": "Season After Days", "type": "integer" }, "season_lead_days": { "default": 30, "description": "How many days before each season's day the row starts showing.", "maximum": 90.0, "minimum": 0.0, "title": "Season Lead Days", "type": "integer" }, "seasons": { "description": "Seasons this row follows (see GET /api/seasons). Empty means it is not seasonal.", "items": { "type": "string" }, "title": "Seasons", "type": "array" }, "seed_window": { "default": 1, "maximum": 20.0, "minimum": 1.0, "title": "Seed Window", "type": "integer" }, "show_days": { "description": "Days this row appears, as ISO weekdays (1=Monday .. 7=Sunday). Empty means every day.", "items": { "type": "integer" }, "title": "Show Days", "type": "array" }, "size": { "default": 15, "maximum": 40.0, "minimum": 5.0, "title": "Size", "type": "integer" }, "sort_order": { "default": 0, "title": "Sort Order", "type": "integer" }, "sort_title_prefix": { "default": "", "description": "Put before the row's name to make its Plex sort title, e.g. '!010_'. Orders the row in the library's Collections tab, not on Home. Empty leaves the sort title alone.", "maxLength": 64, "title": "Sort Title Prefix", "type": "string" }, "theme_days": { "anyOf": [ { "maximum": 90.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "description": "How many days a theme lasts in Explore; null is 7.", "title": "Theme Days" }, "theme_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "description": "The theme this AI row follows (see POST /api/themes). Null for an ordinary row.", "title": "Theme Id" }, "theme_mode": { "default": "fixed", "description": "fixed keeps one theme; explore picks a new one for each person on a schedule.", "enum": [ "fixed", "explore" ], "title": "Theme Mode", "type": "string" }, "unstarted_only": { "default": false, "title": "Unstarted Only", "type": "boolean" }, "watched_pct": { "anyOf": [ { "maximum": 1.0, "minimum": 0.0, "type": "number" }, { "type": "null" } ], "title": "Watched Pct" } }, "required": [ "name" ], "title": "CollectionIn", "type": "object" }, "CollectionOut": { "additionalProperties": true, "description": "A curated-row definition \u2014 the response shape of :func:`serialize_row` (``services/row_editing.py``).", "properties": { "ai_instructions": { "$ref": "#/components/schemas/AiInstructionsOut" }, "ai_paused": { "description": "Whether the row's AI is paused: it keeps its theme but spends no tokens.", "title": "Ai Paused", "type": "boolean" }, "ai_tokens": { "description": "Tokens the AI has spent writing this row's themes.", "title": "Ai Tokens", "type": "integer" }, "audience": { "description": "Everyone, or the subset named by audience_user_ids.", "enum": [ "everyone", "subset" ], "title": "Audience", "type": "string" }, "audience_user_ids": { "items": { "type": "integer" }, "title": "Audience User Ids", "type": "array" }, "avoid_rows": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "description": "Slugs of rows whose titles this row keeps out; null is none.", "title": "Avoid Rows" }, "build": { "description": "Who the row is built for: one per person, or one shared row.", "enum": [ "per_person", "shared" ], "title": "Build", "type": "string" }, "candidate_sources": { "items": { "type": "string" }, "title": "Candidate Sources", "type": "array" }, "cold_start": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "What this row does for someone with too little watch history; null inherits the global setting.", "enum": [ "popular", "skip", null ], "title": "Cold Start" }, "description": { "title": "Description", "type": "string" }, "dry_run": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Dry Run" }, "enabled": { "title": "Enabled", "type": "boolean" }, "explore_brief": { "description": "What Explore is asked to look for; blank lets the AI choose.", "title": "Explore Brief", "type": "string" }, "fallback_name": { "title": "Fallback Name", "type": "string" }, "favourite_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Favourite Count" }, "hub_anchor": { "additionalProperties": { "$ref": "#/components/schemas/HubAnchorOut" }, "title": "Hub Anchor", "type": "object" }, "id": { "title": "Id", "type": "integer" }, "idle_hold_days": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Idle Hold Days" }, "last_run_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Last Run Id" }, "library_keys": { "items": { "type": "string" }, "title": "Library Keys", "type": "array" }, "max_runtime": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Max Runtime" }, "max_seeds": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Max Seeds" }, "max_year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Max Year" }, "media": { "description": "Which library types this row builds in.", "enum": [ "both", "movie", "show" ], "title": "Media", "type": "string" }, "min_rating": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Min Rating" }, "min_watchers": { "title": "Min Watchers", "type": "integer" }, "min_year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Min Year" }, "name": { "title": "Name", "type": "string" }, "name_template": { "title": "Name Template", "type": "string" }, "older_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Older Count" }, "pick_order": { "description": "How the delivered collection is ordered.", "enum": [ "best", "new_first", "newest", "rating", "rotate", "shuffle" ], "title": "Pick Order", "type": "string" }, "pin_top": { "title": "Pin Top", "type": "boolean" }, "placement": { "description": "Where the OWNER's own collection appears.", "enum": [ "both", "home", "library", "off" ], "title": "Placement", "type": "string" }, "placement_friends": { "description": "Where each FRIEND's own collection appears.", "enum": [ "both", "home", "library", "off" ], "title": "Placement Friends", "type": "string" }, "plan": { "anyOf": [ { "items": { "$ref": "#/components/schemas/PlanEntryOut" }, "type": "array" }, { "type": "null" } ], "title": "Plan" }, "poster": { "$ref": "#/components/schemas/PosterOut" }, "preview_incomplete": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Incomplete" }, "preview_titles": { "items": { "$ref": "#/components/schemas/PreviewTitleOut" }, "title": "Preview Titles", "type": "array" }, "recency": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Recency" }, "recent_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Recent Count" }, "refresh_days": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Refresh Days" }, "refresh_share": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "description": "Share of picks swapped on a refresh night; null keeps two thirds.", "title": "Refresh Share" }, "repeat_cooldown_days": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "description": "No repeats within this many days; null is off.", "title": "Repeat Cooldown Days" }, "req_auto_min_demand": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Req Auto Min Demand" }, "req_auto_min_rating": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Req Auto Min Rating" }, "req_auto_send": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Req Auto Send" }, "req_auto_user_tag": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "description": "Tag this row's Sonarr/Radarr requests with the wanting person's slug; null inherits the global requests.auto_user_tag.", "title": "Req Auto User Tag" }, "req_language_mode": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "How this row treats a title's original language when requesting: 'any' (one bar for everything), 'prefer' (other languages need a higher rating to auto-send), or 'only' (never request another language); null inherits the global requests.language_mode.", "enum": [ "any", "prefer", "only", null ], "title": "Req Language Mode" }, "req_max_per_row": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Req Max Per Row" }, "req_max_year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Req Max Year" }, "req_min_demand": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Req Min Demand" }, "req_min_rating": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Req Min Rating" }, "req_min_rating_other": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "description": "Rating another language must reach for this row to auto-send it. Null inherits the global requests.min_rating_other, which may itself be unset \u2014 in which case this row derives from its own req_min_rating plus 1.5.", "title": "Req Min Rating Other" }, "req_min_votes": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Req Min Votes" }, "req_min_year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Req Min Year" }, "req_preferred_languages": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "description": "ISO 639-1 codes this row treats as preferred; null inherits the global requests.preferred_languages. An empty list is a row that cleared its languages.", "title": "Req Preferred Languages" }, "req_radarr_quality_profile_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Req Radarr Quality Profile Id" }, "req_radarr_root_folder": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Req Radarr Root Folder" }, "req_season_count": { "anyOf": [ { "maximum": 99.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "description": "How many seasons 'firstSeason'/'lastSeason' take for this row's requests; null inherits the global requests.season_count.", "title": "Req Season Count" }, "req_skip_library_keys": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "description": "Keys of this row's libraries it does not request titles for; null or empty requests for all of them. A library added to Plex later is on by default.", "title": "Req Skip Library Keys" }, "req_sonarr_monitor": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "How much of a show Sonarr monitors for this row's requests (Sonarr's Add Series 'Monitor' choice); null inherits the global requests.sonarr.monitor.", "enum": [ "all", "firstSeason", "lastSeason", "pilot", "none", null ], "title": "Req Sonarr Monitor" }, "req_sonarr_quality_profile_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Req Sonarr Quality Profile Id" }, "req_sonarr_root_folder": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Req Sonarr Root Folder" }, "request_tag": { "title": "Request Tag", "type": "string" }, "requests_row": { "title": "Requests Row", "type": "boolean" }, "requests_tag_pattern": { "title": "Requests Tag Pattern", "type": "string" }, "requests_window_days": { "title": "Requests Window Days", "type": "integer" }, "rewatch": { "title": "Rewatch", "type": "boolean" }, "rewatch_cooldown_days": { "title": "Rewatch Cooldown Days", "type": "integer" }, "schedule": { "title": "Schedule", "type": "string" }, "season_after_days": { "description": "How many days after each season's day the row stays up.", "title": "Season After Days", "type": "integer" }, "season_lead_days": { "description": "How many days before each season's day the row starts showing.", "title": "Season Lead Days", "type": "integer" }, "season_status": { "anyOf": [ { "$ref": "#/components/schemas/SeasonStatusOut" }, { "type": "null" } ], "description": "Where the row is in its calendar today; null for a row that follows no season." }, "seasons": { "description": "Seasons this row follows, in calendar order. Empty means not seasonal.", "items": { "type": "string" }, "title": "Seasons", "type": "array" }, "seed_window": { "title": "Seed Window", "type": "integer" }, "show_days": { "description": "Days this row appears, as ISO weekdays (1=Monday .. 7=Sunday). Empty means every day.", "items": { "type": "integer" }, "title": "Show Days", "type": "array" }, "shown_today": { "description": "Whether this row is on its surfaces today, judged on the SERVER's clock \u2014 which is the clock the midnight schedule and Plex follow, not the viewer's.", "title": "Shown Today", "type": "boolean" }, "size": { "title": "Size", "type": "integer" }, "slug": { "title": "Slug", "type": "string" }, "sort_order": { "title": "Sort Order", "type": "integer" }, "sort_title_prefix": { "title": "Sort Title Prefix", "type": "string" }, "theme_days": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "description": "Days a theme lasts in Explore; null is 7.", "title": "Theme Days" }, "theme_emoji": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "The fixed theme's emoji; null when it has none, or for Explore.", "title": "Theme Emoji" }, "theme_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "description": "The theme an AI row follows; null for an ordinary row.", "title": "Theme Id" }, "theme_mode": { "description": "Whether an AI row keeps one theme or explores.", "enum": [ "fixed", "explore" ], "title": "Theme Mode", "type": "string" }, "theme_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "The fixed theme's name; null for an ordinary row or an Explore row.", "title": "Theme Name" }, "unstarted_only": { "title": "Unstarted Only", "type": "boolean" }, "watched_pct": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Watched Pct" } }, "required": [ "id", "slug", "name", "last_run_id", "preview_titles", "build", "audience", "audience_user_ids", "enabled", "schedule", "size", "media", "sort_order", "name_template", "fallback_name", "description", "sort_title_prefix", "min_watchers", "request_tag", "candidate_sources", "watched_pct", "rewatch", "rewatch_cooldown_days", "requests_row", "requests_window_days", "requests_tag_pattern", "unstarted_only", "refresh_days", "idle_hold_days", "recency", "recent_count", "favourite_count", "older_count", "max_seeds", "max_runtime", "min_year", "max_year", "min_rating", "cold_start", "seed_window", "req_min_rating", "req_min_votes", "req_min_demand", "req_min_year", "req_max_year", "req_auto_send", "req_auto_min_demand", "req_auto_min_rating", "req_max_per_row", "req_radarr_quality_profile_id", "req_radarr_root_folder", "req_sonarr_quality_profile_id", "req_sonarr_root_folder", "req_sonarr_monitor", "req_season_count", "req_language_mode", "req_preferred_languages", "req_skip_library_keys", "req_min_rating_other", "pick_order", "placement", "placement_friends", "show_days", "shown_today", "seasons", "season_lead_days", "season_after_days", "season_status", "pin_top", "hub_anchor", "library_keys", "poster", "ai_instructions", "theme_id", "theme_name", "theme_emoji", "ai_paused", "ai_tokens", "theme_mode", "explore_brief", "theme_days", "refresh_share", "repeat_cooldown_days", "avoid_rows" ], "title": "CollectionOut", "type": "object" }, "ConfiguredDestination": { "additionalProperties": true, "description": "A credential-free, currently configured service endpoint.", "properties": { "destination_id": { "title": "Destination Id", "type": "string" }, "host": { "title": "Host", "type": "string" }, "label": { "title": "Label", "type": "string" }, "purposes": { "items": { "type": "string" }, "title": "Purposes", "type": "array" }, "service_id": { "title": "Service Id", "type": "string" } }, "required": [ "service_id", "label", "purposes", "destination_id", "host" ], "title": "ConfiguredDestination", "type": "object" }, "ConnectionTestOut": { "additionalProperties": true, "description": "`message` is plain English either way \u2014 the success line, or a redacted failure (rule 9).", "properties": { "message": { "title": "Message", "type": "string" }, "ok": { "title": "Ok", "type": "boolean" } }, "required": [ "ok", "message" ], "title": "ConnectionTestOut", "type": "object" }, "ConsentDecisionIn": { "additionalProperties": false, "description": "One exact browser consent decision protected by an independent CSRF token.", "properties": { "approved": { "title": "Approved", "type": "boolean" }, "csrf_token": { "title": "Csrf Token", "type": "string" }, "flow_id": { "title": "Flow Id", "type": "string" }, "grant_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Grant Id" } }, "required": [ "flow_id", "csrf_token", "approved" ], "title": "ConsentDecisionIn", "type": "object" }, "ConstraintsIn": { "additionalProperties": false, "description": "Strict owner-selected resource bounds for a grant.", "properties": { "destination_ids": { "items": { "type": "string" }, "title": "Destination Ids", "type": "array", "uniqueItems": true }, "include_future_libraries": { "default": true, "title": "Include Future Libraries", "type": "boolean" }, "include_future_rows": { "default": true, "title": "Include Future Rows", "type": "boolean" }, "library_keys": { "items": { "type": "string" }, "title": "Library Keys", "type": "array", "uniqueItems": true }, "max_batch_size": { "anyOf": [ { "maximum": 1000.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "default": 25, "title": "Max Batch Size" }, "max_provider_calls": { "default": 0, "maximum": 100.0, "minimum": 0.0, "title": "Max Provider Calls", "type": "integer" }, "max_work_per_operation": { "anyOf": [ { "maximum": 100000.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Max Work Per Operation" }, "row_ids": { "items": { "type": "integer" }, "title": "Row Ids", "type": "array", "uniqueItems": true }, "setting_groups": { "items": { "type": "string" }, "title": "Setting Groups", "type": "array", "uniqueItems": true } }, "title": "ConstraintsIn", "type": "object" }, "ConstraintsPatchIn": { "additionalProperties": false, "description": "Sparse owner-selected changes to a grant's resource bounds.", "properties": { "destination_ids": { "items": { "type": "string" }, "title": "Destination Ids", "type": "array", "uniqueItems": true }, "include_future_libraries": { "default": false, "title": "Include Future Libraries", "type": "boolean" }, "include_future_rows": { "default": false, "title": "Include Future Rows", "type": "boolean" }, "library_keys": { "items": { "type": "string" }, "title": "Library Keys", "type": "array", "uniqueItems": true }, "max_batch_size": { "anyOf": [ { "maximum": 1000.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Max Batch Size" }, "max_provider_calls": { "default": 0, "maximum": 100.0, "minimum": 0.0, "title": "Max Provider Calls", "type": "integer" }, "max_work_per_operation": { "anyOf": [ { "maximum": 100000.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Max Work Per Operation" }, "row_ids": { "items": { "type": "integer" }, "title": "Row Ids", "type": "array", "uniqueItems": true }, "setting_groups": { "items": { "type": "string" }, "title": "Setting Groups", "type": "array", "uniqueItems": true } }, "title": "ConstraintsPatchIn", "type": "object" }, "CoverageOut": { "additionalProperties": true, "description": "Who is actually covered. `users_enabled`/`rows_enabled` describe the server as it is NOW and\nare deliberately not windowed.", "properties": { "rows_enabled": { "title": "Rows Enabled", "type": "integer" }, "users_enabled": { "title": "Users Enabled", "type": "integer" }, "users_idle": { "title": "Users Idle", "type": "integer" }, "users_total": { "title": "Users Total", "type": "integer" }, "users_watched": { "title": "Users Watched", "type": "integer" }, "users_watched_delta": { "anyOf": [ { "type": "integer" }, { "type": "number" }, { "type": "null" } ], "title": "Users Watched Delta" }, "users_with_picks": { "title": "Users With Picks", "type": "integer" } }, "required": [ "users_enabled", "users_total", "users_with_picks", "users_idle", "users_watched", "users_watched_delta", "rows_enabled" ], "title": "CoverageOut", "type": "object" }, "CuratorModelsOut": { "additionalProperties": true, "description": "The provider the listing was made for (so a stale reply can be told apart from a live one),\nand its model ids. Best-effort: `models` is empty when the provider cannot be asked.", "properties": { "models": { "items": { "type": "string" }, "title": "Models", "type": "array" }, "provider": { "title": "Provider", "type": "string" } }, "required": [ "provider", "models" ], "title": "CuratorModelsOut", "type": "object" }, "CuratorModelsRequest": { "description": "Optional live overrides from the settings form so the model picker can list the provider being\nedited BEFORE it's saved. Any blank field falls back to the saved setting; a redacted api key\n('\u2022\u2022\u2022\u2022\u2022') means 'use the saved key'. The key is used only to build the client in memory \u2014 never\nlogged (only the exception class is).", "properties": { "api_key": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Api Key" }, "ollama_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Ollama Url" }, "provider": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Provider" } }, "title": "CuratorModelsRequest", "type": "object" }, "DateRuleIO": { "additionalProperties": true, "description": "When a season falls: see `seasons.DateRule`. ``weekday`` is Monday=0; ``nth`` is 1-4, or -1 for the last.", "properties": { "day": { "default": 1, "title": "Day", "type": "integer" }, "kind": { "enum": [ "fixed", "nth", "easter", "month" ], "title": "Kind", "type": "string" }, "month": { "default": 1, "title": "Month", "type": "integer" }, "nth": { "default": 1, "title": "Nth", "type": "integer" }, "offset": { "default": 0, "title": "Offset", "type": "integer" }, "weekday": { "default": 0, "title": "Weekday", "type": "integer" } }, "required": [ "kind" ], "title": "DateRuleIO", "type": "object" }, "DeletedOut": { "additionalProperties": true, "properties": { "deleted": { "title": "Deleted", "type": "integer" } }, "required": [ "deleted" ], "title": "DeletedOut", "type": "object" }, "DeletedRowOut": { "additionalProperties": true, "description": "History belonging to a row that no longer exists.", "properties": { "first_seen": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "First Seen" }, "last_seen": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Seen" }, "picks": { "title": "Picks", "type": "integer" }, "slug": { "title": "Slug", "type": "string" } }, "required": [ "slug", "picks", "first_seen", "last_seen" ], "title": "DeletedRowOut", "type": "object" }, "DestinationSelection": { "additionalProperties": false, "description": "The exact catalog choice the owner saw before saving.", "properties": { "destination_id": { "title": "Destination Id", "type": "string" }, "service_id": { "title": "Service Id", "type": "string" } }, "required": [ "service_id", "destination_id" ], "title": "DestinationSelection", "type": "object" }, "Dismiss": { "properties": { "id": { "title": "Id", "type": "string" } }, "required": [ "id" ], "title": "Dismiss", "type": "object" }, "DismissedOut": { "additionalProperties": true, "properties": { "ok": { "title": "Ok", "type": "boolean" } }, "required": [ "ok" ], "title": "DismissedOut", "type": "object" }, "DynamicClientRegistrationIn": { "description": "RFC 7591 subset for public authorization-code clients.", "properties": { "client_name": { "maxLength": 255, "minLength": 1, "title": "Client Name", "type": "string" }, "grant_types": { "items": { "type": "string" }, "title": "Grant Types", "type": "array" }, "redirect_uris": { "items": { "maxLength": 2048, "minLength": 1, "type": "string" }, "maxItems": 10, "minItems": 1, "title": "Redirect Uris", "type": "array" }, "response_types": { "items": { "type": "string" }, "title": "Response Types", "type": "array" }, "token_endpoint_auth_method": { "default": "none", "title": "Token Endpoint Auth Method", "type": "string" } }, "required": [ "client_name", "redirect_uris" ], "title": "DynamicClientRegistrationIn", "type": "object" }, "Effect": { "description": "Observable consequence classes used by planning and authorization.", "enum": [ "local_config", "local_state", "scheduler_change", "external_read", "provider_spend", "plex_read", "plex_write", "plex_privacy_write", "acquisition_write", "notification_send", "credential_change", "personal_data_disclosure" ], "title": "Effect", "type": "string" }, "EffectReferenceOut": { "additionalProperties": true, "description": "Public consequence description.", "properties": { "description": { "title": "Description", "type": "string" }, "kind": { "$ref": "#/components/schemas/Effect" }, "timing": { "$ref": "#/components/schemas/EffectTiming" } }, "required": [ "kind", "timing", "description" ], "title": "EffectReferenceOut", "type": "object" }, "EffectTiming": { "description": "When an effect can occur after a catalog-backed change.", "enum": [ "immediate", "queued", "future_run", "recurring" ], "title": "EffectTiming", "type": "string" }, "EffectivenessReportOut": { "additionalProperties": true, "description": "The dashboard tracking report for one window.", "properties": { "coverage": { "$ref": "#/components/schemas/CoverageOut" }, "first_pick": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "First Pick" }, "overall": { "$ref": "#/components/schemas/OverallOut" }, "per_row": { "items": { "$ref": "#/components/schemas/PerRowOut" }, "title": "Per Row", "type": "array" }, "per_user": { "items": { "$ref": "#/components/schemas/PerUserOut" }, "title": "Per User", "type": "array" }, "recent": { "items": { "$ref": "#/components/schemas/RecentWatchOut" }, "title": "Recent", "type": "array" }, "requests": { "$ref": "#/components/schemas/ReportRequestsOut" }, "runs": { "$ref": "#/components/schemas/ReportRunsOut" }, "since": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Since" }, "top_titles": { "items": { "$ref": "#/components/schemas/TopTitleOut" }, "title": "Top Titles", "type": "array" }, "trend": { "items": { "$ref": "#/components/schemas/TrendPointOut" }, "title": "Trend", "type": "array" }, "watch_sync": { "$ref": "#/components/schemas/WatchSyncOut" }, "window": { "enum": [ "7", "30", "90", "all" ], "title": "Window", "type": "string" }, "window_days": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Window Days" } }, "required": [ "window", "window_days", "since", "first_pick", "overall", "watch_sync", "coverage", "runs", "requests", "trend", "per_user", "per_row", "top_titles", "recent" ], "title": "EffectivenessReportOut", "type": "object" }, "EnforcementOut": { "additionalProperties": true, "description": "The last time a run looked through a real account's eyes at their Home screen.\n\n`measured` is the whole point. An empty `not_enforced` on an unmeasured run is not \"all clear\" \u2014\nit is \"nobody looked\", and rendering the two the same is how a live alert gets cleared.", "properties": { "measured": { "title": "Measured", "type": "boolean" }, "measured_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Measured At" }, "not_enforced": { "additionalProperties": { "items": { "type": "integer" }, "type": "array" }, "title": "Not Enforced", "type": "object" }, "run_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Run Id" }, "unhideable": { "additionalProperties": { "items": { "type": "integer" }, "type": "array" }, "title": "Unhideable", "type": "object" }, "unhideable_measured": { "title": "Unhideable Measured", "type": "boolean" }, "unhideable_measured_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Unhideable Measured At" }, "unhideable_run_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Unhideable Run Id" } }, "required": [ "measured", "run_id", "measured_at", "not_enforced", "unhideable", "unhideable_measured", "unhideable_run_id", "unhideable_measured_at" ], "title": "EnforcementOut", "type": "object" }, "EngagementOut": { "additionalProperties": true, "properties": { "people": { "items": { "$ref": "#/components/schemas/EngagementPersonOut" }, "title": "People", "type": "array" }, "window": { "title": "Window", "type": "string" } }, "required": [ "window", "people" ], "title": "EngagementOut", "type": "object" }, "EngagementPersonOut": { "additionalProperties": true, "properties": { "display_name": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Display Name" }, "picks": { "items": { "$ref": "#/components/schemas/EngagementPickOut" }, "title": "Picks", "type": "array" }, "total": { "title": "Total", "type": "integer" }, "username": { "title": "Username", "type": "string" } }, "required": [ "username", "display_name", "picks", "total" ], "title": "EngagementPersonOut", "type": "object" }, "EngagementPickOut": { "additionalProperties": true, "description": "One pick and what became of it.\n\n`percent` is NULL where no live session ever observed the play \u2014 which is not 0%. Plex's watched\nflag cannot see a partial play at all, so \"we never watched this happen\" and \"they bailed at the\nstart\" are genuinely different states and are not collapsed.", "properties": { "finished_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Finished At" }, "media_type": { "title": "Media Type", "type": "string" }, "observed_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Observed At" }, "outcome": { "title": "Outcome", "type": "string" }, "percent": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Percent" }, "row": { "title": "Row", "type": "string" }, "title": { "title": "Title", "type": "string" }, "watched_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Watched At" } }, "required": [ "title", "row", "media_type", "outcome", "percent", "watched_at", "finished_at", "observed_at" ], "title": "EngagementPickOut", "type": "object" }, "EventOut": { "additionalProperties": true, "description": "One audit row. `message` is the writer's structured diff, and its keys depend on `scope`.", "properties": { "id": { "title": "Id", "type": "integer" }, "level": { "title": "Level", "type": "string" }, "message": { "additionalProperties": true, "title": "Message", "type": "object" }, "scope": { "title": "Scope", "type": "string" }, "ts": { "title": "Ts", "type": "string" } }, "required": [ "id", "ts", "level", "scope", "message" ], "title": "EventOut", "type": "object" }, "GrantConstraintsOut": { "additionalProperties": true, "description": "Publicly effective resource and work bounds.", "properties": { "destination_ids": { "items": { "type": "string" }, "title": "Destination Ids", "type": "array" }, "include_future_libraries": { "title": "Include Future Libraries", "type": "boolean" }, "include_future_rows": { "title": "Include Future Rows", "type": "boolean" }, "library_keys": { "items": { "type": "string" }, "title": "Library Keys", "type": "array" }, "max_batch_size": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Max Batch Size" }, "max_provider_calls": { "title": "Max Provider Calls", "type": "integer" }, "max_work_per_operation": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Max Work Per Operation" }, "owner_managed": { "title": "Owner Managed", "type": "boolean" }, "row_ids": { "items": { "type": "integer" }, "title": "Row Ids", "type": "array" }, "setting_groups": { "items": { "type": "string" }, "title": "Setting Groups", "type": "array" } }, "required": [ "row_ids", "library_keys", "setting_groups", "destination_ids", "include_future_rows", "include_future_libraries", "max_batch_size", "max_work_per_operation", "max_provider_calls", "owner_managed" ], "title": "GrantConstraintsOut", "type": "object" }, "GrantConstraintsPatchIn": { "additionalProperties": false, "description": "Revision-guarded owner change to one grant's authority.", "properties": { "access_role": { "anyOf": [ { "enum": [ "view", "manage" ], "type": "string" }, { "type": "null" } ], "title": "Access Role" }, "approve_updated_access": { "default": false, "title": "Approve Updated Access", "type": "boolean" }, "capabilities": { "anyOf": [ { "items": { "$ref": "#/components/schemas/Capability" }, "type": "array", "uniqueItems": true }, { "type": "null" } ], "title": "Capabilities" }, "constraints": { "anyOf": [ { "$ref": "#/components/schemas/ConstraintsPatchIn" }, { "type": "null" } ] }, "expected_revision": { "exclusiveMinimum": 0.0, "title": "Expected Revision", "type": "integer" }, "max_provider_calls": { "anyOf": [ { "maximum": 100.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Max Provider Calls" }, "paid_enabled": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Paid Enabled" }, "selected_destinations": { "items": { "$ref": "#/components/schemas/DestinationSelection" }, "title": "Selected Destinations", "type": "array" }, "upgrade_owner_managed": { "default": false, "title": "Upgrade Owner Managed", "type": "boolean" } }, "required": [ "expected_revision" ], "title": "GrantConstraintsPatchIn", "type": "object" }, "GrantCreateIn": { "additionalProperties": false, "description": "Owner-approved named assistant grant.", "properties": { "access_role": { "anyOf": [ { "enum": [ "view", "manage" ], "type": "string" }, { "type": "null" } ], "title": "Access Role" }, "capabilities": { "anyOf": [ { "items": { "$ref": "#/components/schemas/Capability" }, "type": "array", "uniqueItems": true }, { "type": "null" } ], "title": "Capabilities" }, "client_id": { "maxLength": 255, "minLength": 1, "title": "Client Id", "type": "string" }, "constraints": { "$ref": "#/components/schemas/ConstraintsIn" }, "expires_in_days": { "anyOf": [ { "maximum": 365.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "default": 90, "title": "Expires In Days" }, "name": { "maxLength": 255, "minLength": 1, "title": "Name", "type": "string" }, "owner_managed": { "default": false, "title": "Owner Managed", "type": "boolean" }, "preset": { "$ref": "#/components/schemas/GrantPreset" }, "selected_destinations": { "items": { "$ref": "#/components/schemas/DestinationSelection" }, "title": "Selected Destinations", "type": "array" } }, "required": [ "client_id", "name", "preset" ], "title": "GrantCreateIn", "type": "object" }, "GrantOut": { "additionalProperties": true, "description": "Safe browser-facing named grant authority.", "properties": { "access_role": { "anyOf": [ { "enum": [ "view", "manage" ], "type": "string" }, { "type": "null" } ], "title": "Access Role" }, "capabilities": { "items": { "$ref": "#/components/schemas/Capability" }, "title": "Capabilities", "type": "array" }, "client_id": { "title": "Client Id", "type": "string" }, "constraints": { "$ref": "#/components/schemas/GrantConstraintsOut" }, "expires_at": { "anyOf": [ { "format": "date-time", "type": "string" }, { "type": "null" } ], "title": "Expires At" }, "full_management": { "title": "Full Management", "type": "boolean" }, "id": { "title": "Id", "type": "string" }, "name": { "title": "Name", "type": "string" }, "owner_account_id": { "title": "Owner Account Id", "type": "integer" }, "preset": { "$ref": "#/components/schemas/GrantPreset" }, "requires_access_approval": { "title": "Requires Access Approval", "type": "boolean" }, "revision": { "title": "Revision", "type": "integer" } }, "required": [ "id", "owner_account_id", "client_id", "name", "preset", "capabilities", "constraints", "requires_access_approval", "full_management", "access_role", "revision", "expires_at" ], "title": "GrantOut", "type": "object" }, "GrantPreset": { "description": "Owner-facing starting points for a named assistant connection.", "enum": [ "inspect", "manage_selected_rows", "owner_automation" ], "title": "GrantPreset", "type": "string" }, "GrantSummaryOut": { "additionalProperties": true, "description": "Grant authority plus owner-visible activity and call accounting.", "properties": { "access_role": { "anyOf": [ { "enum": [ "view", "manage" ], "type": "string" }, { "type": "null" } ], "title": "Access Role" }, "capabilities": { "items": { "$ref": "#/components/schemas/Capability" }, "title": "Capabilities", "type": "array" }, "client_id": { "title": "Client Id", "type": "string" }, "constraints": { "$ref": "#/components/schemas/GrantConstraintsOut" }, "created_at": { "format": "date-time", "title": "Created At", "type": "string" }, "expires_at": { "anyOf": [ { "format": "date-time", "type": "string" }, { "type": "null" } ], "title": "Expires At" }, "full_management": { "title": "Full Management", "type": "boolean" }, "id": { "title": "Id", "type": "string" }, "last_used_at": { "anyOf": [ { "format": "date-time", "type": "string" }, { "type": "null" } ], "title": "Last Used At" }, "local_credential_count": { "title": "Local Credential Count", "type": "integer" }, "name": { "title": "Name", "type": "string" }, "owner_account_id": { "title": "Owner Account Id", "type": "integer" }, "preset": { "$ref": "#/components/schemas/GrantPreset" }, "provider_call_quota": { "$ref": "#/components/schemas/ProviderCallQuotaOut" }, "requires_access_approval": { "title": "Requires Access Approval", "type": "boolean" }, "revision": { "title": "Revision", "type": "integer" }, "revoked_at": { "anyOf": [ { "format": "date-time", "type": "string" }, { "type": "null" } ], "title": "Revoked At" }, "updated_at": { "format": "date-time", "title": "Updated At", "type": "string" } }, "required": [ "id", "owner_account_id", "client_id", "name", "preset", "capabilities", "constraints", "requires_access_approval", "full_management", "access_role", "revision", "expires_at", "created_at", "updated_at", "revoked_at", "last_used_at", "local_credential_count", "provider_call_quota" ], "title": "GrantSummaryOut", "type": "object" }, "HTTPValidationError": { "properties": { "detail": { "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail", "type": "array" } }, "title": "HTTPValidationError", "type": "object" }, "HealthOut": { "additionalProperties": true, "properties": { "status": { "title": "Status", "type": "string" } }, "required": [ "status" ], "title": "HealthOut", "type": "object" }, "HeldTitleOut": { "additionalProperties": true, "properties": { "reason": { "title": "Reason", "type": "string" }, "story": { "title": "Story", "type": "boolean" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "tmdb_id", "title", "year", "reason", "story" ], "title": "HeldTitleOut", "type": "object" }, "HistoryMixItemOut": { "additionalProperties": true, "description": "One title in a person's history mix.", "properties": { "media_type": { "title": "Media Type", "type": "string" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Tmdb Id" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "title", "year", "media_type", "tmdb_id" ], "title": "HistoryMixItemOut", "type": "object" }, "HistoryMixOut": { "additionalProperties": true, "description": "The titles one row's AI web search draws from a person's history this week, in their three groups.", "properties": { "favourite_count": { "title": "Favourite Count", "type": "integer" }, "favourites": { "items": { "$ref": "#/components/schemas/HistoryMixItemOut" }, "title": "Favourites", "type": "array" }, "older": { "items": { "$ref": "#/components/schemas/HistoryMixItemOut" }, "title": "Older", "type": "array" }, "older_count": { "title": "Older Count", "type": "integer" }, "recent": { "items": { "$ref": "#/components/schemas/HistoryMixItemOut" }, "title": "Recent", "type": "array" } }, "required": [ "recent", "favourites", "older", "favourite_count", "older_count" ], "title": "HistoryMixOut", "type": "object" }, "HoldPreviewIn": { "description": "Genres and tags the owner is considering, before they are saved.", "properties": { "genres": { "items": { "type": "integer" }, "maxItems": 30, "title": "Genres", "type": "array" }, "tags": { "items": { "type": "integer" }, "maxItems": 60, "title": "Tags", "type": "array" } }, "title": "HoldPreviewIn", "type": "object" }, "HoldPreviewOut": { "additionalProperties": true, "properties": { "checked": { "title": "Checked", "type": "integer" }, "held": { "items": { "$ref": "#/components/schemas/HeldTitleOut" }, "title": "Held", "type": "array" }, "unread": { "title": "Unread", "type": "integer" } }, "required": [ "checked", "held", "unread" ], "title": "HoldPreviewOut", "type": "object" }, "HomeUserOut": { "additionalProperties": true, "description": "A Home user that could become the owner's watching account.", "properties": { "already_a_shortlist_user": { "title": "Already A Shortlist User", "type": "boolean" }, "plex_account_id": { "title": "Plex Account Id", "type": "integer" }, "protected": { "title": "Protected", "type": "boolean" }, "title": { "title": "Title", "type": "string" } }, "required": [ "plex_account_id", "title", "protected", "already_a_shortlist_user" ], "title": "HomeUserOut", "type": "object" }, "HubAnchorIn": { "description": "A per-library shelf placement for one row: the very TOP (``top``), or after/before either\nanother Shortlist ROW (``row``, a row slug) or a foreign collection (``anchor``, a title).\n``top`` needs neither; otherwise exactly one of ``row``/``anchor`` must be set. ``enabled``\nfalse is a placement in its own right \u2014 \"never position this row\" \u2014 and needs neither.\n\n``row`` is a slug rather than a title because a per-person row is one Plex collection PER PERSON:\na title names one account's copy and is meaningless for everyone else, which is what made the\npicker offer forty identical-looking options and place none of them (issue #81).", "properties": { "anchor": { "default": "", "maxLength": 255, "title": "Anchor", "type": "string" }, "before": { "default": false, "title": "Before", "type": "boolean" }, "enabled": { "default": true, "title": "Enabled", "type": "boolean" }, "row": { "default": "", "maxLength": 255, "title": "Row", "type": "string" }, "top": { "default": false, "title": "Top", "type": "boolean" } }, "title": "HubAnchorIn", "type": "object" }, "HubAnchorOut": { "additionalProperties": true, "description": "A stored shelf placement. Defaulted, unlike the rest of these response fields: rows saved\nbefore ``top`` existed have only ``anchor``/``before``, and filling in the same default\n`HubAnchorIn` would have written is what those rows already mean.", "properties": { "anchor": { "default": "", "title": "Anchor", "type": "string" }, "before": { "default": false, "title": "Before", "type": "boolean" }, "row": { "default": "", "title": "Row", "type": "string" }, "top": { "default": false, "title": "Top", "type": "boolean" } }, "title": "HubAnchorOut", "type": "object" }, "ImageProviderOut": { "additionalProperties": true, "properties": { "capable": { "title": "Capable", "type": "boolean" }, "provider": { "title": "Provider", "type": "string" }, "reason": { "title": "Reason", "type": "string" } }, "required": [ "capable", "provider", "reason" ], "title": "ImageProviderOut", "type": "object" }, "JobCatalogEntryOut": { "additionalProperties": true, "description": "One card on the Jobs page: what the kind does, when it next runs, and how it went last time.", "properties": { "description": { "title": "Description", "type": "string" }, "failed": { "title": "Failed", "type": "integer" }, "kind": { "title": "Kind", "type": "string" }, "label": { "title": "Label", "type": "string" }, "last": { "anyOf": [ { "$ref": "#/components/schemas/JobOut" }, { "type": "null" } ] }, "manual": { "title": "Manual", "type": "boolean" }, "next_run": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Next Run" }, "queued": { "title": "Queued", "type": "integer" }, "running": { "title": "Running", "type": "integer" }, "schedule_optional": { "title": "Schedule Optional", "type": "boolean" }, "schedule_setting": { "title": "Schedule Setting", "type": "string" }, "scheduled": { "title": "Scheduled", "type": "boolean" }, "total": { "title": "Total", "type": "integer" }, "trigger": { "title": "Trigger", "type": "string" } }, "required": [ "kind", "label", "description", "manual", "trigger", "scheduled", "schedule_optional", "schedule_setting", "next_run", "last", "total", "queued", "running", "failed" ], "title": "JobCatalogEntryOut", "type": "object" }, "JobOut": { "additionalProperties": true, "description": "One background job row \u2014 the shape `_job_dict` builds, on both `/jobs` and `/jobs/catalog`.", "properties": { "attempts": { "title": "Attempts", "type": "integer" }, "created_at": { "title": "Created At", "type": "string" }, "detail": { "title": "Detail", "type": "string" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "finished_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Finished At" }, "id": { "title": "Id", "type": "integer" }, "kind": { "title": "Kind", "type": "string" }, "max_attempts": { "title": "Max Attempts", "type": "integer" }, "payload": { "additionalProperties": true, "title": "Payload", "type": "object" }, "result": { "additionalProperties": true, "title": "Result", "type": "object" }, "started_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Started At" }, "status": { "enum": [ "queued", "running", "done", "failed" ], "title": "Status", "type": "string" } }, "required": [ "id", "kind", "status", "attempts", "max_attempts", "detail", "error", "payload", "result", "created_at", "started_at", "finished_at" ], "title": "JobOut", "type": "object" }, "JobRunOut": { "additionalProperties": true, "description": "The receipt for `POST /jobs` \u2014 a subset of `JobOut` plus the sync-check preview lists.", "properties": { "detail": { "title": "Detail", "type": "string" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "fixed": { "items": { "type": "string" }, "title": "Fixed", "type": "array" }, "id": { "title": "Id", "type": "integer" }, "kind": { "title": "Kind", "type": "string" }, "orphans": { "items": { "type": "string" }, "title": "Orphans", "type": "array" }, "status": { "enum": [ "queued", "running", "done", "failed" ], "title": "Status", "type": "string" } }, "required": [ "id", "kind", "status", "detail", "error", "fixed", "orphans" ], "title": "JobRunOut", "type": "object" }, "JsonValue": {}, "LandingOut": { "additionalProperties": true, "description": "The landing rate over a matured cohort \u2014 picks old enough to have had their chance.", "properties": { "cohort_from": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Cohort From" }, "cohort_to": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Cohort To" }, "delivered": { "title": "Delivered", "type": "integer" }, "finished": { "title": "Finished", "type": "integer" }, "finished_rate": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Finished Rate" }, "matured_days": { "title": "Matured Days", "type": "integer" }, "rate": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Rate" }, "watched": { "title": "Watched", "type": "integer" } }, "required": [ "delivered", "watched", "finished", "finished_rate", "rate", "cohort_from", "cohort_to", "matured_days" ], "title": "LandingOut", "type": "object" }, "LibraryCollectionOut": { "additionalProperties": true, "description": "A candidate anchor. Title, because the shelf is ordered by title, not by rating key \u2014 plus\nwhether it has a position on a Plex shelf at all, which decides if it can anchor anything.", "properties": { "on_shelf": { "title": "On Shelf", "type": "boolean" }, "title": { "title": "Title", "type": "string" } }, "required": [ "title", "on_shelf" ], "title": "LibraryCollectionOut", "type": "object" }, "LibraryOut": { "additionalProperties": true, "properties": { "key": { "title": "Key", "type": "string" }, "title": { "title": "Title", "type": "string" }, "type": { "enum": [ "movie", "show" ], "title": "Type", "type": "string" } }, "required": [ "key", "title", "type" ], "title": "LibraryOut", "type": "object" }, "LibrarySectionOut": { "additionalProperties": true, "description": "One movie/show library the probe found.", "properties": { "count": { "title": "Count", "type": "integer" }, "key": { "title": "Key", "type": "integer" }, "title": { "title": "Title", "type": "string" }, "type": { "enum": [ "movie", "show" ], "title": "Type", "type": "string" } }, "required": [ "key", "title", "type", "count" ], "title": "LibrarySectionOut", "type": "object" }, "LibraryTitleOut": { "additionalProperties": true, "properties": { "media_type": { "enum": [ "movie", "show" ], "title": "Media Type", "type": "string" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "tmdb_id", "media_type", "title", "year" ], "title": "LibraryTitleOut", "type": "object" }, "LinkRequest": { "properties": { "machine_id": { "title": "Machine Id", "type": "string" }, "owner_account_id": { "title": "Owner Account Id", "type": "integer" }, "plex_pass": { "default": false, "title": "Plex Pass", "type": "boolean" }, "plex_url": { "title": "Plex Url", "type": "string" }, "server_name": { "default": "", "title": "Server Name", "type": "string" }, "version": { "default": "", "title": "Version", "type": "string" } }, "required": [ "plex_url", "machine_id", "owner_account_id" ], "title": "LinkRequest", "type": "object" }, "LinkedOut": { "additionalProperties": true, "properties": { "linked": { "title": "Linked", "type": "boolean" }, "server_name": { "title": "Server Name", "type": "string" } }, "required": [ "linked", "server_name" ], "title": "LinkedOut", "type": "object" }, "LocalCredentialIn": { "additionalProperties": false, "description": "Lifetime for a one-time local credential handoff.", "properties": { "expires_in_days": { "default": 90, "maximum": 365.0, "minimum": 1.0, "title": "Expires In Days", "type": "integer" } }, "title": "LocalCredentialIn", "type": "object" }, "LogLineOut": { "additionalProperties": true, "description": "One parsed log entry. `ts` is None for a line the parser could not date (a raw traceback).", "properties": { "level": { "title": "Level", "type": "string" }, "message": { "title": "Message", "type": "string" }, "source": { "title": "Source", "type": "string" }, "ts": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Ts" } }, "required": [ "ts", "level", "source", "message" ], "title": "LogLineOut", "type": "object" }, "LogoutOut": { "additionalProperties": true, "properties": { "ok": { "title": "Ok", "type": "boolean" } }, "required": [ "ok" ], "title": "LogoutOut", "type": "object" }, "LogsOut": { "additionalProperties": true, "properties": { "file": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "File" }, "lines": { "items": { "$ref": "#/components/schemas/LogLineOut" }, "title": "Lines", "type": "array" }, "total_matched": { "title": "Total Matched", "type": "integer" }, "truncated": { "title": "Truncated", "type": "boolean" } }, "required": [ "lines", "total_matched", "truncated", "file" ], "title": "LogsOut", "type": "object" }, "NotificationOut": { "additionalProperties": true, "description": "One alert as the React bell renders it \u2014 plain text throughout, no HTML.\n\n``extra=\"allow\"`` (here and on the wrapper) is not optional: a strict Pydantic response model\nsilently DROPS any key it does not declare, so a field missed here would vanish from the payload\ninstead of failing loudly.", "properties": { "action_label": { "title": "Action Label", "type": "string" }, "action_url": { "title": "Action Url", "type": "string" }, "body": { "title": "Body", "type": "string" }, "dismissable": { "title": "Dismissable", "type": "boolean" }, "id": { "title": "Id", "type": "string" }, "severity": { "enum": [ "info", "warning", "error" ], "title": "Severity", "type": "string" }, "title": { "title": "Title", "type": "string" } }, "required": [ "id", "severity", "title", "body", "action_url", "action_label", "dismissable" ], "title": "NotificationOut", "type": "object" }, "NotificationsOut": { "additionalProperties": true, "properties": { "dismissed": { "items": { "type": "string" }, "title": "Dismissed", "type": "array" }, "notifications": { "items": { "$ref": "#/components/schemas/NotificationOut" }, "title": "Notifications", "type": "array" } }, "required": [ "notifications", "dismissed" ], "title": "NotificationsOut", "type": "object" }, "NumericRangeOut": { "additionalProperties": true, "description": "Public numeric bounds.", "properties": { "maximum": { "anyOf": [ { "type": "number" }, { "type": "integer" } ], "title": "Maximum" }, "minimum": { "anyOf": [ { "type": "number" }, { "type": "integer" } ], "title": "Minimum" }, "unit": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Unit" } }, "required": [ "minimum", "maximum" ], "title": "NumericRangeOut", "type": "object" }, "OAuthAuthorizeIn": { "additionalProperties": false, "description": "Bounded authorization request copied from the browser URL by the SPA.", "properties": { "client_id": { "maxLength": 255, "minLength": 1, "title": "Client Id", "type": "string" }, "code_challenge": { "maxLength": 128, "minLength": 43, "title": "Code Challenge", "type": "string" }, "code_challenge_method": { "default": "S256", "title": "Code Challenge Method", "type": "string" }, "redirect_uri": { "maxLength": 2048, "minLength": 1, "title": "Redirect Uri", "type": "string" }, "resource": { "maxLength": 2048, "minLength": 1, "title": "Resource", "type": "string" }, "response_type": { "default": "code", "title": "Response Type", "type": "string" }, "scope": { "default": "instance.read", "maxLength": 2048, "title": "Scope", "type": "string" }, "state": { "maxLength": 2048, "minLength": 1, "title": "State", "type": "string" } }, "required": [ "client_id", "redirect_uri", "resource", "state", "code_challenge" ], "title": "OAuthAuthorizeIn", "type": "object" }, "OverallOut": { "additionalProperties": true, "description": "Headline counts for the window, with the change vs the previous equal period.", "properties": { "avg_days_to_watch": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Avg Days To Watch" }, "avg_days_to_watch_delta": { "anyOf": [ { "type": "integer" }, { "type": "number" }, { "type": "null" } ], "title": "Avg Days To Watch Delta" }, "bounced": { "title": "Bounced", "type": "integer" }, "delivered": { "title": "Delivered", "type": "integer" }, "dropped": { "title": "Dropped", "type": "integer" }, "finished": { "title": "Finished", "type": "integer" }, "landing": { "$ref": "#/components/schemas/LandingOut" }, "viewing_share": { "$ref": "#/components/schemas/ViewingShareOut" }, "watched": { "title": "Watched", "type": "integer" }, "watched_delta": { "anyOf": [ { "type": "integer" }, { "type": "number" }, { "type": "null" } ], "title": "Watched Delta" }, "watched_prev": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Watched Prev" } }, "required": [ "delivered", "watched", "watched_prev", "watched_delta", "finished", "bounced", "dropped", "avg_days_to_watch", "avg_days_to_watch_delta", "landing", "viewing_share" ], "title": "OverallOut", "type": "object" }, "OwnedCollectionOut": { "additionalProperties": true, "properties": { "kind": { "enum": [ "user", "shared" ], "title": "Kind", "type": "string" }, "label": { "title": "Label", "type": "string" }, "library": { "title": "Library", "type": "string" }, "orphan": { "title": "Orphan", "type": "boolean" }, "rating_key": { "title": "Rating Key", "type": "integer" }, "slug": { "title": "Slug", "type": "string" }, "title": { "title": "Title", "type": "string" } }, "required": [ "library", "title", "label", "rating_key", "kind", "slug", "orphan" ], "title": "OwnedCollectionOut", "type": "object" }, "OwnedCollectionsOut": { "additionalProperties": true, "properties": { "collections": { "items": { "$ref": "#/components/schemas/OwnedCollectionOut" }, "title": "Collections", "type": "array" }, "orphans": { "title": "Orphans", "type": "integer" }, "total": { "title": "Total", "type": "integer" } }, "required": [ "collections", "total", "orphans" ], "title": "OwnedCollectionsOut", "type": "object" }, "PendingRestore": { "additionalProperties": true, "properties": { "backup": { "title": "Backup", "type": "string" }, "requested_at": { "title": "Requested At", "type": "string" } }, "required": [ "backup", "requested_at" ], "title": "PendingRestore", "type": "object" }, "PendingRestoreOut": { "additionalProperties": true, "description": "The restore waiting for a restart, if any.", "properties": { "pending": { "anyOf": [ { "$ref": "#/components/schemas/PendingRestore" }, { "type": "null" } ] } }, "required": [ "pending" ], "title": "PendingRestoreOut", "type": "object" }, "PerRowOut": { "additionalProperties": true, "description": "One (row, library) line. A row targeting >1 library is one Plex collection per library.", "properties": { "deleted": { "title": "Deleted", "type": "boolean" }, "delivered": { "title": "Delivered", "type": "integer" }, "finished": { "title": "Finished", "type": "integer" }, "library": { "title": "Library", "type": "string" }, "name": { "title": "Name", "type": "string" }, "section_key": { "title": "Section Key", "type": "string" }, "slug": { "title": "Slug", "type": "string" }, "watched": { "title": "Watched", "type": "integer" } }, "required": [ "slug", "section_key", "library", "name", "deleted", "delivered", "watched", "finished" ], "title": "PerRowOut", "type": "object" }, "PerUserOut": { "additionalProperties": true, "properties": { "delivered": { "title": "Delivered", "type": "integer" }, "display_name": { "title": "Display Name", "type": "string" }, "enabled": { "title": "Enabled", "type": "boolean" }, "finished": { "title": "Finished", "type": "integer" }, "id": { "title": "Id", "type": "integer" }, "slug": { "title": "Slug", "type": "string" }, "username": { "title": "Username", "type": "string" }, "watched": { "title": "Watched", "type": "integer" } }, "required": [ "id", "username", "display_name", "slug", "enabled", "delivered", "watched", "finished" ], "title": "PerUserOut", "type": "object" }, "PersonReadyOut": { "additionalProperties": true, "properties": { "display_name": { "title": "Display Name", "type": "string" }, "linked": { "title": "Linked", "type": "boolean" }, "ready": { "title": "Ready", "type": "integer" }, "user_id": { "title": "User Id", "type": "integer" } }, "required": [ "user_id", "display_name", "linked", "ready" ], "title": "PersonReadyOut", "type": "object" }, "PickIO": { "additionalProperties": true, "description": "A title the owner picked by hand.", "properties": { "media_type": { "enum": [ "movie", "show" ], "title": "Media Type", "type": "string" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "tmdb_id", "media_type", "title" ], "title": "PickIO", "type": "object" }, "PickOut": { "additionalProperties": true, "description": "One delivered recommendation, as the run detail lists it.", "properties": { "affinity": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Affinity" }, "order_rating": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Order Rating" }, "order_rating_source": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Order Rating Source" }, "rank": { "title": "Rank", "type": "integer" }, "rating": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Rating" }, "rating_key": { "default": 0, "title": "Rating Key", "type": "integer" }, "rating_source": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Rating Source" }, "reason": { "title": "Reason", "type": "string" }, "seed_title": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Seed Title" }, "sources": { "items": { "type": "string" }, "title": "Sources", "type": "array" }, "title": { "title": "Title", "type": "string" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "rank", "title", "reason", "seed_title", "sources", "affinity" ], "title": "PickOut", "type": "object" }, "PinOut": { "additionalProperties": true, "description": "A freshly created plex.tv PIN. The `code` is what the owner types into plex.tv/link \u2014 it is\nshort-lived and useless without the same `client_id`, and it is not a Shortlist credential.\n\n``extra=\"allow\"`` is on every response model in this file: a strict Pydantic response model\nsilently DROPS any key it does not declare, so a field missed here would vanish from the payload\nrather than fail loudly. The model documents the shape; it never filters it.", "properties": { "client_id": { "title": "Client Id", "type": "string" }, "code": { "title": "Code", "type": "string" }, "id": { "title": "Id", "type": "integer" } }, "required": [ "id", "code", "client_id" ], "title": "PinOut", "type": "object" }, "PinStatusOut": { "additionalProperties": true, "description": "The poll result. Until the owner approves in Plex it is `linked: false` and the identity\nfields are null; the Plex auth token is NEVER part of this payload (it is held server-side,\nkeyed to the session, so an XSS in the SPA cannot steal it).", "properties": { "account_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Account Id" }, "linked": { "title": "Linked", "type": "boolean" }, "username": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Username" } }, "required": [ "linked" ], "title": "PinStatusOut", "type": "object" }, "PlanEntryOut": { "additionalProperties": true, "description": "One unit of Plex work an edit would cause.", "properties": { "collections": { "items": { "type": "string" }, "title": "Collections", "type": "array" }, "in_sections": { "items": { "type": "string" }, "title": "In Sections", "type": "array" }, "kind": { "description": "What this step would do.", "enum": [ "poster_reset", "privacy_sync", "reconcile", "rename", "visibility" ], "title": "Kind", "type": "string" }, "only_user_ids": { "items": { "type": "integer" }, "title": "Only User Ids", "type": "array" }, "reason": { "title": "Reason", "type": "string" } }, "required": [ "kind", "reason", "collections", "only_user_ids", "in_sections" ], "title": "PlanEntryOut", "type": "object" }, "PlexCollectionOut": { "additionalProperties": true, "properties": { "count": { "title": "Count", "type": "integer" }, "media_type": { "enum": [ "movie", "show" ], "title": "Media Type", "type": "string" }, "section_key": { "title": "Section Key", "type": "string" }, "section_title": { "title": "Section Title", "type": "string" }, "smart": { "title": "Smart", "type": "boolean" }, "title": { "title": "Title", "type": "string" } }, "required": [ "section_key", "section_title", "title", "count", "smart", "media_type" ], "title": "PlexCollectionOut", "type": "object" }, "PlexServerOut": { "additionalProperties": true, "description": "A server plex.tv says this account can reach, with every advertised address already tried.", "properties": { "connections": { "items": { "$ref": "#/components/schemas/ServerConnectionOut" }, "title": "Connections", "type": "array" }, "machine_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Machine Id" }, "name": { "title": "Name", "type": "string" }, "owned": { "title": "Owned", "type": "boolean" }, "version": { "title": "Version", "type": "string" } }, "required": [ "name", "machine_id", "owned", "version", "connections" ], "title": "PlexServerOut", "type": "object" }, "PosterIn": { "description": "A row's custom-poster config. ``mode`` \"\" leaves Plex artwork alone; \"upload\" uses the image\nstored via the upload endpoint; \"text\" renders ``title``/``subtitle`` with the built-in Pillow\nengine (no AI); \"ai\" renders them with the curator provider's image model. The text fields share\nthe row-name placeholders ({user}/{library_name}/{top_seed}).", "properties": { "mode": { "default": "", "description": "Poster source; \"\" leaves Plex artwork alone.", "enum": [ "", "ai", "generate", "text", "upload" ], "title": "Mode", "type": "string" }, "style": { "default": "", "maxLength": 400, "title": "Style", "type": "string" }, "subtitle": { "default": "", "maxLength": 120, "title": "Subtitle", "type": "string" }, "title": { "default": "", "maxLength": 120, "title": "Title", "type": "string" } }, "title": "PosterIn", "type": "object" }, "PosterOut": { "additionalProperties": true, "description": "A row's poster config as the editor reads it \u2014 never the image bytes.", "properties": { "has_image": { "title": "Has Image", "type": "boolean" }, "mode": { "description": "Poster source; \"\" leaves Plex artwork alone.", "enum": [ "", "ai", "generate", "text", "upload" ], "title": "Mode", "type": "string" }, "style": { "title": "Style", "type": "string" }, "subtitle": { "title": "Subtitle", "type": "string" }, "title": { "title": "Title", "type": "string" } }, "required": [ "mode", "title", "subtitle", "style", "has_image" ], "title": "PosterOut", "type": "object" }, "PosterUploadOut": { "additionalProperties": true, "properties": { "mode": { "title": "Mode", "type": "string" }, "ok": { "title": "Ok", "type": "boolean" } }, "required": [ "ok", "mode" ], "title": "PosterUploadOut", "type": "object" }, "PresetOut": { "additionalProperties": true, "description": "A ready-made season's save fields plus catalogue metadata. ``preset`` is ``key``.", "properties": { "after_days": { "default": 0, "maximum": 30.0, "minimum": 0.0, "title": "After Days", "type": "integer" }, "category": { "default": "holidays", "enum": [ "holidays", "film_days", "spotlights" ], "title": "Category", "type": "string" }, "collections": { "items": { "$ref": "#/components/schemas/CollectionIO" }, "maxItems": 10, "title": "Collections", "type": "array" }, "description": { "default": "", "title": "Description", "type": "string" }, "emoji": { "maxLength": 8, "minLength": 1, "title": "Emoji", "type": "string" }, "excluded_genres": { "items": { "type": "integer" }, "maxItems": 5, "title": "Excluded Genres", "type": "array" }, "genre": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Genre" }, "key": { "title": "Key", "type": "string" }, "label": { "title": "Label", "type": "string" }, "lead_days": { "default": 7, "maximum": 90.0, "minimum": 0.0, "title": "Lead Days", "type": "integer" }, "name": { "maxLength": 40, "minLength": 1, "title": "Name", "type": "string" }, "note": { "title": "Note", "type": "string" }, "picks": { "items": { "$ref": "#/components/schemas/PickIO" }, "maxItems": 200, "title": "Picks", "type": "array" }, "preset": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preset" }, "rule": { "$ref": "#/components/schemas/DateRuleIO" }, "tags": { "items": { "$ref": "#/components/schemas/TagIO" }, "maxItems": 20, "title": "Tags", "type": "array" } }, "required": [ "name", "emoji", "rule", "key", "label", "note" ], "title": "PresetOut", "type": "object" }, "PreviewIn": { "properties": { "brief": { "default": "", "maxLength": 1000, "title": "Brief", "type": "string" }, "change": { "default": "", "maxLength": 1000, "title": "Change", "type": "string" }, "collection_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Collection Id" }, "current_draft": { "anyOf": [ { "$ref": "#/components/schemas/ThemeIn" }, { "type": "null" } ] }, "current_theme_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Current Theme Id" }, "guidance": { "default": "", "maxLength": 4000, "title": "Guidance", "type": "string" }, "media": { "default": "both", "enum": [ "movie", "show", "both" ], "title": "Media", "type": "string" } }, "title": "PreviewIn", "type": "object" }, "PreviewOut": { "additionalProperties": true, "properties": { "diff": { "anyOf": [ { "$ref": "#/components/schemas/ThemeDiffOut" }, { "type": "null" } ] }, "draft": { "$ref": "#/components/schemas/ThemeOut" }, "stats": { "$ref": "#/components/schemas/ThemeStatsOut" }, "tokens": { "title": "Tokens", "type": "integer" } }, "required": [ "draft", "stats", "diff", "tokens" ], "title": "PreviewOut", "type": "object" }, "PreviewTitleOut": { "additionalProperties": true, "description": "One title from a row's latest delivery, for the Rows list's poster collage.", "properties": { "rating_key": { "title": "Rating Key", "type": "integer" }, "title": { "title": "Title", "type": "string" } }, "required": [ "rating_key", "title" ], "title": "PreviewTitleOut", "type": "object" }, "PrivacyStatusOut": { "additionalProperties": true, "description": "One live reading of the whole server's sharing state.", "properties": { "accounts": { "items": { "$ref": "#/components/schemas/AccountPrivacyOut" }, "title": "Accounts", "type": "array" }, "enforcement": { "$ref": "#/components/schemas/EnforcementOut" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "read_at": { "title": "Read At", "type": "string" }, "rows_error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Rows Error" }, "rows_on_plex": { "items": { "type": "string" }, "title": "Rows On Plex", "type": "array" }, "snapshots_kept": { "title": "Snapshots Kept", "type": "integer" }, "summary": { "title": "Summary", "type": "string" } }, "required": [ "read_at", "summary", "accounts", "rows_on_plex", "rows_error", "error", "enforcement", "snapshots_kept" ], "title": "PrivacyStatusOut", "type": "object" }, "ProbeCheckOut": { "additionalProperties": true, "description": "One line of the wizard's step-1 checklist: did it pass, and what to say about it.\n\nThis is the one shape here with an OPTIONAL field, which is why its route sets\n``response_model_exclude_unset``. `extra=\"allow\"` stops a model dropping a key, but nothing stops\nit INVENTING one: without that flag every check the probe sends without a `value` \u2014 and the whole\n`tautulli` entry on a server that has none \u2014 would arrive as an explicit `null` the handler never\nwrote. The flag makes the payload byte-identical to the dict, which is the entire contract these\nmodels are meant to keep.", "properties": { "message": { "title": "Message", "type": "string" }, "ok": { "title": "Ok", "type": "boolean" }, "value": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Value" } }, "required": [ "ok", "message" ], "title": "ProbeCheckOut", "type": "object" }, "ProbeChecksOut": { "additionalProperties": true, "description": "The checklist itself.\n\n`tautulli` runs only when the form supplied a Tautulli URL, so it is the one check that can be\nabsent \u2014 and it therefore needs the default. Without one it would be REQUIRED, and every probe of\na server with no Tautulli would fail response validation with a 500 instead of rendering.", "properties": { "libraries": { "$ref": "#/components/schemas/ProbeCheckOut" }, "plex_pass": { "$ref": "#/components/schemas/ProbeCheckOut" }, "pms_version": { "$ref": "#/components/schemas/ProbeCheckOut" }, "tautulli": { "anyOf": [ { "$ref": "#/components/schemas/ProbeCheckOut" }, { "type": "null" } ] } }, "required": [ "pms_version", "plex_pass", "libraries" ], "title": "ProbeChecksOut", "type": "object" }, "ProbeRequest": { "properties": { "plex_url": { "title": "Plex Url", "type": "string" }, "tautulli_apikey": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Tautulli Apikey" }, "tautulli_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Tautulli Url" } }, "required": [ "plex_url" ], "title": "ProbeRequest", "type": "object" }, "ProbeResultOut": { "additionalProperties": true, "description": "What `POST /setup/probe` answers: the checklist plus what it learned about the server.", "properties": { "checks": { "$ref": "#/components/schemas/ProbeChecksOut" }, "libraries": { "items": { "$ref": "#/components/schemas/LibrarySectionOut" }, "title": "Libraries", "type": "array" }, "machine_id": { "title": "Machine Id", "type": "string" }, "owner_account_id": { "title": "Owner Account Id", "type": "integer" }, "server_name": { "title": "Server Name", "type": "string" } }, "required": [ "checks", "machine_id", "server_name", "owner_account_id", "libraries" ], "title": "ProbeResultOut", "type": "object" }, "PromptsOut": { "additionalProperties": true, "properties": { "guidance": { "title": "Guidance", "type": "string" }, "mechanics": { "title": "Mechanics", "type": "string" } }, "required": [ "guidance", "mechanics" ], "title": "PromptsOut", "type": "object" }, "ProviderCallQuotaOut": { "additionalProperties": true, "description": "Current conservative lifetime call accounting.", "properties": { "lifetime_limit": { "title": "Lifetime Limit", "type": "integer" }, "remaining": { "title": "Remaining", "type": "integer" }, "reserved": { "title": "Reserved", "type": "integer" } }, "required": [ "lifetime_limit", "reserved", "remaining" ], "title": "ProviderCallQuotaOut", "type": "object" }, "QualityProfileOut": { "additionalProperties": true, "properties": { "id": { "title": "Id", "type": "integer" }, "name": { "title": "Name", "type": "string" } }, "required": [ "id", "name" ], "title": "QualityProfileOut", "type": "object" }, "RecentWatchOut": { "additionalProperties": true, "properties": { "display_name": { "title": "Display Name", "type": "string" }, "finished_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Finished At" }, "library": { "title": "Library", "type": "string" }, "media_type": { "title": "Media Type", "type": "string" }, "rating_key": { "title": "Rating Key", "type": "integer" }, "row": { "title": "Row", "type": "string" }, "seed_title": { "title": "Seed Title", "type": "string" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "user_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "User Id" }, "username": { "title": "Username", "type": "string" }, "watched_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Watched At" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "user_id", "username", "display_name", "title", "media_type", "tmdb_id", "rating_key", "year", "row", "library", "seed_title", "watched_at", "finished_at" ], "title": "RecentWatchOut", "type": "object" }, "RegenerateRequest": { "additionalProperties": false, "properties": { "user_id": { "title": "User Id", "type": "integer" } }, "required": [ "user_id" ], "title": "RegenerateRequest", "type": "object" }, "RejectedOut": { "additionalProperties": true, "description": "How many rows the action actually touched \u2014 not how many ids were sent. Each of the four\ninbox actions skips the statuses it does not own, so the count is the only honest receipt.\n\n``extra=\"allow\"`` is on every response model here (and every nested one): a strict model would\nsilently DROP any key the handler returns but the model has not declared, so a field missed\nhere would vanish from the payload rather than fail loudly.", "properties": { "rejected": { "title": "Rejected", "type": "integer" } }, "required": [ "rejected" ], "title": "RejectedOut", "type": "object" }, "ReleaseNotesOut": { "additionalProperties": true, "properties": { "notes": { "title": "Notes", "type": "string" }, "published_at": { "title": "Published At", "type": "string" }, "url": { "title": "Url", "type": "string" }, "version": { "title": "Version", "type": "string" } }, "required": [ "version", "url", "published_at", "notes" ], "title": "ReleaseNotesOut", "type": "object" }, "RemovedOut": { "additionalProperties": true, "description": "What `DELETE /users/{id}` dropped. `user_id` is still valid \u2014 the row is archived, not deleted.", "properties": { "picks_deleted": { "title": "Picks Deleted", "type": "integer" }, "runs_deleted": { "title": "Runs Deleted", "type": "integer" }, "user_id": { "title": "User Id", "type": "integer" } }, "required": [ "user_id", "picks_deleted", "runs_deleted" ], "title": "RemovedOut", "type": "object" }, "RenameRequest": { "properties": { "dry_run": { "default": false, "title": "Dry Run", "type": "boolean" }, "name_template": { "default": "", "title": "Name Template", "type": "string" }, "old_template": { "default": "", "title": "Old Template", "type": "string" } }, "title": "RenameRequest", "type": "object" }, "ReportRequestsOut": { "additionalProperties": true, "properties": { "pending": { "title": "Pending", "type": "integer" }, "sent": { "title": "Sent", "type": "integer" }, "watched_after_sent": { "title": "Watched After Sent", "type": "integer" } }, "required": [ "sent", "pending", "watched_after_sent" ], "title": "ReportRequestsOut", "type": "object" }, "ReportRunsOut": { "additionalProperties": true, "properties": { "errors_last": { "title": "Errors Last", "type": "integer" }, "in_window": { "title": "In Window", "type": "integer" }, "in_window_delta": { "anyOf": [ { "type": "integer" }, { "type": "number" }, { "type": "null" } ], "title": "In Window Delta" }, "last_finished": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Finished" }, "last_status": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Status" }, "total": { "title": "Total", "type": "integer" } }, "required": [ "total", "in_window", "in_window_delta", "last_finished", "last_status", "errors_last" ], "title": "ReportRunsOut", "type": "object" }, "RequestAction": { "properties": { "dry_run": { "default": false, "title": "Dry Run", "type": "boolean" }, "ids": { "items": { "type": "integer" }, "maxItems": 1000, "title": "Ids", "type": "array" } }, "required": [ "ids" ], "title": "RequestAction", "type": "object" }, "RequestCandidateOut": { "additionalProperties": true, "properties": { "arr_slug": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Arr Slug" }, "demand": { "title": "Demand", "type": "integer" }, "detail": { "title": "Detail", "type": "string" }, "excluded": { "default": false, "title": "Excluded", "type": "boolean" }, "id": { "title": "Id", "type": "integer" }, "imdb_id": { "default": "", "title": "Imdb Id", "type": "string" }, "language": { "default": "", "title": "Language", "type": "string" }, "media_type": { "title": "Media Type", "type": "string" }, "overview": { "default": "", "title": "Overview", "type": "string" }, "poster_path": { "default": "", "title": "Poster Path", "type": "string" }, "rating": { "title": "Rating", "type": "number" }, "row_slug": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Row Slug" }, "status": { "title": "Status", "type": "string" }, "tags": { "items": { "type": "string" }, "title": "Tags", "type": "array" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "updated_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Updated At" }, "vote_count": { "title": "Vote Count", "type": "integer" }, "wanters": { "items": { "type": "string" }, "title": "Wanters", "type": "array" }, "why": { "items": { "$ref": "#/components/schemas/RequestWhyOut" }, "title": "Why", "type": "array" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "id", "tmdb_id", "media_type", "title", "year", "rating", "vote_count", "demand", "tags", "wanters", "why", "status", "detail", "updated_at" ], "title": "RequestCandidateOut", "type": "object" }, "RequestWhyOut": { "additionalProperties": true, "properties": { "row": { "title": "Row", "type": "string" }, "seed": { "title": "Seed", "type": "string" }, "source": { "title": "Source", "type": "string" }, "user": { "title": "User", "type": "string" } }, "required": [ "user", "row", "seed", "source" ], "title": "RequestWhyOut", "type": "object" }, "ResetBehavior": { "description": "How a caller expresses the absence of a stored setting value.", "enum": [ "restore_default", "literal_null", "unavailable" ], "title": "ResetBehavior", "type": "string" }, "RestoreRequest": { "properties": { "name": { "title": "Name", "type": "string" } }, "required": [ "name" ], "title": "RestoreRequest", "type": "object" }, "RestoredOut": { "additionalProperties": true, "properties": { "restored": { "title": "Restored", "type": "integer" } }, "required": [ "restored" ], "title": "RestoredOut", "type": "object" }, "RootFolderOut": { "additionalProperties": true, "properties": { "id": { "title": "Id", "type": "integer" }, "path": { "title": "Path", "type": "string" } }, "required": [ "id", "path" ], "title": "RootFolderOut", "type": "object" }, "RotationTargetOut": { "additionalProperties": true, "properties": { "current": { "anyOf": [ { "$ref": "#/components/schemas/ThemeRefOut" }, { "type": "null" } ] }, "history": { "description": "Their earlier themes on this row, newest first.", "items": { "$ref": "#/components/schemas/ThemeRefOut" }, "title": "History", "type": "array" }, "name": { "title": "Name", "type": "string" }, "next": { "anyOf": [ { "$ref": "#/components/schemas/ThemeRefOut" }, { "type": "null" } ], "description": "The theme queued to start when the current one ends." }, "next_due_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "When the current theme ends and the next one starts.", "title": "Next Due At" }, "started_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "When the current theme started.", "title": "Started At" }, "user_id": { "title": "User Id", "type": "integer" } }, "required": [ "user_id", "name", "current", "next", "started_at", "next_due_at", "history" ], "title": "RotationTargetOut", "type": "object" }, "RowDeletePreviewOut": { "additionalProperties": true, "description": "What `DELETE /collections/{id}?dry_run=true` WOULD do. Nothing is written.", "properties": { "anchors_cleared": { "items": { "type": "string" }, "title": "Anchors Cleared", "type": "array" }, "collections": { "items": { "type": "string" }, "title": "Collections", "type": "array" }, "dry_run": { "title": "Dry Run", "type": "boolean" }, "message": { "title": "Message", "type": "string" }, "preview_incomplete": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Incomplete" }, "privacy_sync": { "title": "Privacy Sync", "type": "boolean" }, "schedule_cleared": { "title": "Schedule Cleared", "type": "boolean" } }, "required": [ "dry_run", "collections", "anchors_cleared", "privacy_sync", "schedule_cleared", "preview_incomplete", "message" ], "title": "RowDeletePreviewOut", "type": "object" }, "RowEffectivenessOut": { "additionalProperties": true, "description": "Whether one row is working, for the panel beside its settings.", "properties": { "delivered": { "title": "Delivered", "type": "integer" }, "finished": { "title": "Finished", "type": "integer" }, "first_delivered_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "First Delivered At" }, "last_delivered_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Delivered At" }, "matured": { "anyOf": [ { "$ref": "#/components/schemas/RowMaturedCohort" }, { "type": "null" } ] }, "matured_days": { "title": "Matured Days", "type": "integer" }, "per_library": { "items": { "$ref": "#/components/schemas/RowLibraryEffectiveness" }, "title": "Per Library", "type": "array" }, "runs": { "title": "Runs", "type": "integer" }, "shared_titles": { "anyOf": [ { "additionalProperties": { "type": "integer" }, "type": "object" }, { "type": "null" } ], "title": "Shared Titles" }, "watched": { "title": "Watched", "type": "integer" } }, "required": [ "delivered", "watched", "finished", "runs", "first_delivered_at", "last_delivered_at", "matured_days", "shared_titles", "matured", "per_library" ], "title": "RowEffectivenessOut", "type": "object" }, "RowLibraryEffectiveness": { "additionalProperties": true, "description": "One library's share of a row's matured cohort. A row that builds in two libraries is two Plex\ncollections and genuinely performs differently in each, so they are never merged into one line.", "properties": { "delivered": { "title": "Delivered", "type": "integer" }, "finished": { "title": "Finished", "type": "integer" }, "library": { "title": "Library", "type": "string" }, "rate": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Rate" }, "watched": { "title": "Watched", "type": "integer" } }, "required": [ "library", "delivered", "watched", "finished", "rate" ], "title": "RowLibraryEffectiveness", "type": "object" }, "RowMaturedCohort": { "additionalProperties": true, "description": "The picks old enough to be judged: delivered at least `matured_days` ago, so every one of them\nhas had its full window to be watched.", "properties": { "cohort_to": { "title": "Cohort To", "type": "string" }, "delivered": { "title": "Delivered", "type": "integer" }, "finished": { "title": "Finished", "type": "integer" }, "rate": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Rate" }, "watched": { "title": "Watched", "type": "integer" } }, "required": [ "delivered", "watched", "finished", "rate", "cohort_to" ], "title": "RowMaturedCohort", "type": "object" }, "RowOverrideOut": { "additionalProperties": true, "description": "This person's stored tweaks for one row. `None` on either field means \"use the row's own\".", "properties": { "favourite_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Favourite Count" }, "older_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Older Count" }, "recent_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Recent Count" }, "row_size": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Row Size" } }, "required": [ "row_size", "recent_count", "favourite_count", "older_count" ], "title": "RowOverrideOut", "type": "object" }, "RowOverridePatch": { "additionalProperties": false, "description": "PATCH-shaped stored preference values shared by REST and assistant planning.", "properties": { "favourite_count": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Favourite Count" }, "muted": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Muted" }, "older_count": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Older Count" }, "recent_count": { "anyOf": [ { "maximum": 25.0, "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Recent Count" }, "row_size": { "anyOf": [ { "maximum": 40.0, "minimum": 5.0, "type": "integer" }, { "type": "null" } ], "title": "Row Size" } }, "title": "RowOverridePatch", "type": "object" }, "RowOverrideSavedOut": { "additionalProperties": true, "description": "The override as stored, echoed back by the PUT.", "properties": { "collection_id": { "title": "Collection Id", "type": "integer" }, "favourite_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Favourite Count" }, "muted": { "title": "Muted", "type": "boolean" }, "older_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Older Count" }, "recent_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Recent Count" }, "row_size": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Row Size" } }, "required": [ "collection_id", "muted", "row_size", "recent_count", "favourite_count", "older_count" ], "title": "RowOverrideSavedOut", "type": "object" }, "RowSourceServerOut": { "additionalProperties": true, "description": "One Radarr/Sonarr server Overseerr sends to, and whether it stamps the requester's tag.", "properties": { "is4k": { "title": "Is4K", "type": "boolean" }, "kind": { "title": "Kind", "type": "string" }, "name": { "title": "Name", "type": "string" }, "tag_requests": { "title": "Tag Requests", "type": "boolean" } }, "required": [ "kind", "name", "is4k", "tag_requests" ], "title": "RowSourceServerOut", "type": "object" }, "RowSourcesOut": { "additionalProperties": true, "description": "The requests-row setup check: can the row know who asked for what, and for whom?", "properties": { "complete": { "title": "Complete", "type": "boolean" }, "overseerr": { "enum": [ "connected", "unreachable", "off" ], "title": "Overseerr", "type": "string" }, "people": { "items": { "$ref": "#/components/schemas/PersonReadyOut" }, "title": "People", "type": "array" }, "problems": { "items": { "type": "string" }, "title": "Problems", "type": "array" }, "radarr": { "enum": [ "connected", "unreachable", "off" ], "title": "Radarr", "type": "string" }, "seerr_linked": { "title": "Seerr Linked", "type": "integer" }, "seerr_requesters": { "title": "Seerr Requesters", "type": "integer" }, "seerr_requests": { "title": "Seerr Requests", "type": "integer" }, "servers": { "items": { "$ref": "#/components/schemas/RowSourceServerOut" }, "title": "Servers", "type": "array" }, "sonarr": { "enum": [ "connected", "unreachable", "off" ], "title": "Sonarr", "type": "string" }, "tagged_movies": { "title": "Tagged Movies", "type": "integer" }, "tagged_shows": { "title": "Tagged Shows", "type": "integer" }, "tags": { "items": { "$ref": "#/components/schemas/TagMatchOut" }, "title": "Tags", "type": "array" } }, "required": [ "overseerr", "radarr", "sonarr", "complete", "problems", "seerr_requests", "seerr_requesters", "seerr_linked", "servers", "tagged_movies", "tagged_shows", "people", "tags" ], "title": "RowSourcesOut", "type": "object" }, "RowTemplateDefinitionOut": { "additionalProperties": true, "description": "Public row template metadata and creation defaults.", "properties": { "audience_behavior": { "title": "Audience Behavior", "type": "string" }, "changed_fields": { "items": { "type": "string" }, "title": "Changed Fields", "type": "array" }, "description": { "title": "Description", "type": "string" }, "editable_fields": { "items": { "type": "string" }, "title": "Editable Fields", "type": "array" }, "effective_values": { "additionalProperties": { "$ref": "#/components/schemas/JsonValue" }, "title": "Effective Values", "type": "object" }, "effects": { "default": [ "local_config" ], "items": { "$ref": "#/components/schemas/Effect" }, "title": "Effects", "type": "array" }, "emoji": { "title": "Emoji", "type": "string" }, "highlights": { "items": { "type": "string" }, "title": "Highlights", "type": "array" }, "id": { "title": "Id", "type": "string" }, "kind": { "title": "Kind", "type": "string" }, "prerequisites": { "default": [], "items": { "type": "string" }, "title": "Prerequisites", "type": "array" }, "required_capabilities": { "default": [ "rows.create" ], "items": { "$ref": "#/components/schemas/Capability" }, "title": "Required Capabilities", "type": "array" }, "required_services": { "default": [], "items": { "type": "string" }, "title": "Required Services", "type": "array" }, "summary": { "title": "Summary", "type": "string" }, "title": { "title": "Title", "type": "string" }, "values": { "additionalProperties": { "$ref": "#/components/schemas/JsonValue" }, "title": "Values", "type": "object" } }, "required": [ "id", "kind", "emoji", "title", "summary", "description", "highlights", "values", "effective_values", "changed_fields", "editable_fields", "audience_behavior" ], "title": "RowTemplateDefinitionOut", "type": "object" }, "RulesIO": { "additionalProperties": true, "description": "A theme's hard limits. A missing or null one is no limit.", "properties": { "max_runtime": { "anyOf": [ { "minimum": 1.0, "type": "integer" }, { "type": "null" } ], "title": "Max Runtime" }, "max_year": { "anyOf": [ { "maximum": 2200.0, "minimum": 1850.0, "type": "integer" }, { "type": "null" } ], "title": "Max Year" }, "min_rating": { "anyOf": [ { "maximum": 10.0, "minimum": 0.0, "type": "number" }, { "type": "null" } ], "title": "Min Rating" }, "min_votes": { "anyOf": [ { "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Min Votes" }, "min_year": { "anyOf": [ { "maximum": 2200.0, "minimum": 1850.0, "type": "integer" }, { "type": "null" } ], "title": "Min Year" } }, "title": "RulesIO", "type": "object" }, "RunCancelledOut": { "additionalProperties": true, "properties": { "cancelling": { "title": "Cancelling", "type": "boolean" } }, "required": [ "cancelling" ], "title": "RunCancelledOut", "type": "object" }, "RunCreatedOut": { "additionalProperties": true, "properties": { "run_id": { "title": "Run Id", "type": "integer" } }, "required": [ "run_id" ], "title": "RunCreatedOut", "type": "object" }, "RunDetailOut": { "additionalProperties": true, "description": "A run plus every person in it, and every shared row it built.", "properties": { "began_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Began At" }, "dry_run": { "title": "Dry Run", "type": "boolean" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "finished_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Finished At" }, "id": { "title": "Id", "type": "integer" }, "privacy": { "anyOf": [ { "$ref": "#/components/schemas/RunPrivacyOut" }, { "type": "null" } ] }, "promotion_blockers": { "items": { "type": "string" }, "title": "Promotion Blockers", "type": "array" }, "shared_rows": { "items": { "$ref": "#/components/schemas/RunSharedRowOut" }, "title": "Shared Rows", "type": "array" }, "started_at": { "title": "Started At", "type": "string" }, "stats": { "additionalProperties": true, "title": "Stats", "type": "object" }, "status": { "title": "Status", "type": "string" }, "trigger": { "enum": [ "schedule", "manual", "wizard", "resume", "assistant" ], "title": "Trigger", "type": "string" }, "users": { "items": { "$ref": "#/components/schemas/RunUserOut" }, "title": "Users", "type": "array" }, "web_share": { "anyOf": [ { "$ref": "#/components/schemas/WebShareOut" }, { "type": "null" } ] } }, "required": [ "id", "trigger", "started_at", "began_at", "finished_at", "status", "dry_run", "stats", "error", "promotion_blockers", "privacy", "users", "shared_rows" ], "title": "RunDetailOut", "type": "object" }, "RunFinishedEvent": { "additionalProperties": true, "description": "Event ``run.finished`` \u2014 a run reached a terminal state.\n\n`aborted` is a cancel that still completed its privacy merge and promotion, so it is an outcome\nrather than a failure; the UI distinguishes the three.", "properties": { "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "run_id": { "title": "Run Id", "type": "integer" }, "status": { "enum": [ "ok", "error", "aborted" ], "title": "Status", "type": "string" } }, "required": [ "run_id", "status" ], "title": "RunFinishedEvent", "type": "object" }, "RunJobRequest": { "properties": { "background": { "default": false, "title": "Background", "type": "boolean" }, "kind": { "title": "Kind", "type": "string" }, "payload": { "additionalProperties": true, "default": {}, "title": "Payload", "type": "object" } }, "required": [ "kind" ], "title": "RunJobRequest", "type": "object" }, "RunLogLineOut": { "additionalProperties": true, "description": "One line of a run's activity log.\n\nEverything but `seq` is optional because the live in-memory tail hands back exactly what the\nprogress hook pushed, key for key \u2014 only the durable `run_log_lines` read fills every field in.\n`seq` is the exception: the buffer's sink stamps it on the way in, so it is always there.", "properties": { "counts": { "additionalProperties": true, "title": "Counts", "type": "object" }, "level": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Level" }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Reason" }, "run_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Run Id" }, "seq": { "title": "Seq", "type": "integer" }, "stage": { "default": "", "title": "Stage", "type": "string" }, "ts": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Ts" }, "user": { "default": "", "title": "User", "type": "string" } }, "required": [ "seq" ], "title": "RunLogLineOut", "type": "object" }, "RunPrivacyOut": { "additionalProperties": true, "description": "What this run measured about who can see whose rows. Reporting only.", "properties": { "can_see_others": { "items": { "type": "string" }, "title": "Can See Others", "type": "array" }, "filters_not_enforced": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Filters Not Enforced" }, "left_alone": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Left Alone" }, "unchecked": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Unchecked" }, "unreadable_filters": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Unreadable Filters" }, "write_failed": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Write Failed" } }, "required": [ "can_see_others", "unreadable_filters", "filters_not_enforced", "unchecked", "write_failed", "left_alone" ], "title": "RunPrivacyOut", "type": "object" }, "RunProgressEvent": { "additionalProperties": true, "description": "Event ``run.progress`` \u2014 a run entered a non-terminal state. `cancelling` is published the\nmoment /cancel is accepted; the run keeps going until the person it is on finishes.", "properties": { "run_id": { "title": "Run Id", "type": "integer" }, "status": { "enum": [ "running", "cancelling" ], "title": "Status", "type": "string" } }, "required": [ "run_id", "status" ], "title": "RunProgressEvent", "type": "object" }, "RunRequest": { "properties": { "collection_ids": { "anyOf": [ { "items": { "type": "integer" }, "type": "array" }, { "type": "null" } ], "title": "Collection Ids" }, "dry_run": { "default": false, "title": "Dry Run", "type": "boolean" }, "user_ids": { "anyOf": [ { "items": { "type": "integer" }, "type": "array" }, { "type": "null" } ], "title": "User Ids" } }, "title": "RunRequest", "type": "object" }, "RunSharedRowOut": { "additionalProperties": true, "description": "One SHARED row's outcome in a run \u2014 the per-row twin of `RunUserOut`.\n\nA shared row is built once for the whole server from pooled history, so it belongs to nobody and\nnever appears in `users`. Before it had a record of its own, a run whose only work was a shared\nrow rendered as a wall of skipped people with its actual output nowhere on screen.", "properties": { "breakdown": { "items": { "additionalProperties": true, "type": "object" }, "title": "Breakdown", "type": "array" }, "collection_slug": { "title": "Collection Slug", "type": "string" }, "diff": { "additionalProperties": true, "title": "Diff", "type": "object" }, "duration_ms": { "title": "Duration Ms", "type": "integer" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "exa_searches": { "title": "Exa Searches", "type": "integer" }, "has_trace": { "title": "Has Trace", "type": "boolean" }, "llm_tokens": { "title": "Llm Tokens", "type": "integer" }, "llm_tokens_by_step": { "additionalProperties": { "type": "integer" }, "title": "Llm Tokens By Step", "type": "object" }, "picks": { "items": { "$ref": "#/components/schemas/PickOut" }, "title": "Picks", "type": "array" }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Reason" }, "row_title": { "title": "Row Title", "type": "string" }, "status": { "title": "Status", "type": "string" } }, "required": [ "collection_slug", "row_title", "status", "error", "reason", "duration_ms", "llm_tokens", "llm_tokens_by_step", "exa_searches", "diff", "picks", "breakdown", "has_trace" ], "title": "RunSharedRowOut", "type": "object" }, "RunSummaryOut": { "additionalProperties": true, "description": "One run, as the Runs list shows it.", "properties": { "began_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Began At" }, "dry_run": { "title": "Dry Run", "type": "boolean" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "finished_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Finished At" }, "id": { "title": "Id", "type": "integer" }, "privacy": { "anyOf": [ { "$ref": "#/components/schemas/RunPrivacyOut" }, { "type": "null" } ] }, "promotion_blockers": { "items": { "type": "string" }, "title": "Promotion Blockers", "type": "array" }, "started_at": { "title": "Started At", "type": "string" }, "stats": { "additionalProperties": true, "title": "Stats", "type": "object" }, "status": { "title": "Status", "type": "string" }, "trigger": { "enum": [ "schedule", "manual", "wizard", "resume", "assistant" ], "title": "Trigger", "type": "string" } }, "required": [ "id", "trigger", "started_at", "began_at", "finished_at", "status", "dry_run", "stats", "error", "promotion_blockers", "privacy" ], "title": "RunSummaryOut", "type": "object" }, "RunUserOut": { "additionalProperties": true, "description": "One person's outcome in a run. Users who haven't finished yet appear with the same keys and\n`status: \"pending\"`, so the shape does not change mid-run.", "properties": { "breakdown": { "items": { "additionalProperties": true, "type": "object" }, "title": "Breakdown", "type": "array" }, "cost": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "title": "Cost" }, "diff": { "additionalProperties": true, "title": "Diff", "type": "object" }, "display_name": { "title": "Display Name", "type": "string" }, "duration_ms": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Duration Ms" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "exa_searches": { "title": "Exa Searches", "type": "integer" }, "has_trace": { "title": "Has Trace", "type": "boolean" }, "llm_tokens": { "title": "Llm Tokens", "type": "integer" }, "llm_tokens_by_step": { "additionalProperties": { "type": "integer" }, "title": "Llm Tokens By Step", "type": "object" }, "picks": { "items": { "$ref": "#/components/schemas/PickOut" }, "title": "Picks", "type": "array" }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Reason" }, "rows_considered": { "additionalProperties": { "type": "string" }, "title": "Rows Considered", "type": "object" }, "slug": { "title": "Slug", "type": "string" }, "status": { "title": "Status", "type": "string" }, "username": { "title": "Username", "type": "string" } }, "required": [ "username", "display_name", "slug", "status", "error", "reason", "duration_ms", "llm_tokens", "llm_tokens_by_step", "exa_searches", "diff", "picks", "breakdown", "has_trace", "rows_considered", "cost" ], "title": "RunUserOut", "type": "object" }, "RunUserTraceOut": { "additionalProperties": true, "description": "The full pipeline trace for one user in one run.\n\nAlso serves a SHARED row's trace, which has the same stages minus `history` \u2014 a shared row is\nbuilt from pooled watching, so it records `gathers` and no per-person history. `username` and\n`display_name` carry the row's title there, so one trace view renders both without a fork.", "properties": { "breakdown": { "items": { "additionalProperties": true, "type": "object" }, "title": "Breakdown", "type": "array" }, "display_name": { "title": "Display Name", "type": "string" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Reason" }, "requests": { "additionalProperties": { "$ref": "#/components/schemas/TraceRequestOut" }, "title": "Requests", "type": "object" }, "status": { "title": "Status", "type": "string" }, "trace": { "additionalProperties": true, "title": "Trace", "type": "object" }, "username": { "title": "Username", "type": "string" } }, "required": [ "username", "display_name", "status", "error", "reason", "trace", "breakdown", "requests" ], "title": "RunUserTraceOut", "type": "object" }, "RunsDeletedOut": { "additionalProperties": true, "properties": { "deleted": { "title": "Deleted", "type": "integer" } }, "required": [ "deleted" ], "title": "RunsDeletedOut", "type": "object" }, "RunsSummaryOut": { "additionalProperties": true, "description": "Totals for the Runs page header.", "properties": { "error": { "title": "Error", "type": "integer" }, "last_finished": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Finished" }, "last_status": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Status" }, "ok": { "title": "Ok", "type": "integer" }, "total": { "title": "Total", "type": "integer" } }, "required": [ "total", "ok", "error", "last_finished", "last_status" ], "title": "RunsSummaryOut", "type": "object" }, "ScheduleJobOut": { "additionalProperties": true, "description": "One scheduled job.\n\n``extra=\"allow\"`` is on every model in this file, nested ones included: a strict Pydantic\nresponse model silently DROPS undeclared keys from the payload, so a field missed here would\ndisappear from the response instead of failing loudly. The model documents; it never filters.", "properties": { "cron": { "title": "Cron", "type": "string" }, "default_cron": { "title": "Default Cron", "type": "string" }, "description": { "title": "Description", "type": "string" }, "kind": { "title": "Kind", "type": "string" }, "label": { "title": "Label", "type": "string" }, "next_run": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Next Run" }, "optional": { "title": "Optional", "type": "boolean" }, "setting": { "title": "Setting", "type": "string" }, "type": { "title": "Type", "type": "string" }, "using_default": { "title": "Using Default", "type": "boolean" }, "writes_plex": { "title": "Writes Plex", "type": "boolean" } }, "required": [ "type", "kind", "label", "description", "setting", "cron", "using_default", "default_cron", "optional", "writes_plex", "next_run" ], "title": "ScheduleJobOut", "type": "object" }, "ScheduleOut": { "additionalProperties": true, "properties": { "jobs": { "items": { "$ref": "#/components/schemas/ScheduleJobOut" }, "title": "Jobs", "type": "array" }, "rows": { "items": { "$ref": "#/components/schemas/ScheduleRowsOut" }, "title": "Rows", "type": "array" } }, "required": [ "jobs", "rows" ], "title": "ScheduleOut", "type": "object" }, "ScheduleRowOut": { "additionalProperties": true, "properties": { "id": { "title": "Id", "type": "integer" }, "name": { "title": "Name", "type": "string" }, "slug": { "title": "Slug", "type": "string" } }, "required": [ "id", "slug", "name" ], "title": "ScheduleRowOut", "type": "object" }, "ScheduleRowsOut": { "additionalProperties": true, "description": "The rows sharing one cron \u2014 ONE trigger that builds all of them, not one entry each.", "properties": { "cron": { "title": "Cron", "type": "string" }, "next_run": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Next Run" }, "rows": { "items": { "$ref": "#/components/schemas/ScheduleRowOut" }, "title": "Rows", "type": "array" }, "type": { "title": "Type", "type": "string" } }, "required": [ "type", "cron", "rows", "next_run" ], "title": "ScheduleRowsOut", "type": "object" }, "SeasonDateOut": { "additionalProperties": true, "description": "When a date rule next falls (ISO), or, with ``next_date`` None, why the rule can't be used.", "properties": { "next_date": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Next Date" }, "next_windows": { "items": { "$ref": "#/components/schemas/shortlist__server__api__seasons__SeasonWindowOut" }, "title": "Next Windows", "type": "array" }, "rule_error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Rule Error" } }, "required": [ "next_date", "rule_error" ], "title": "SeasonDateOut", "type": "object" }, "SeasonIn": { "additionalProperties": false, "description": "A custom season, to create or replace.", "properties": { "after_days": { "default": 0, "maximum": 30.0, "minimum": 0.0, "title": "After Days", "type": "integer" }, "collections": { "items": { "$ref": "#/components/schemas/CollectionIO" }, "maxItems": 10, "title": "Collections", "type": "array" }, "emoji": { "maxLength": 8, "minLength": 1, "title": "Emoji", "type": "string" }, "excluded_genres": { "items": { "type": "integer" }, "maxItems": 5, "title": "Excluded Genres", "type": "array" }, "genre": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Genre" }, "lead_days": { "default": 7, "maximum": 90.0, "minimum": 0.0, "title": "Lead Days", "type": "integer" }, "name": { "maxLength": 40, "minLength": 1, "title": "Name", "type": "string" }, "picks": { "items": { "$ref": "#/components/schemas/PickIO" }, "maxItems": 200, "title": "Picks", "type": "array" }, "preset": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preset" }, "rule": { "$ref": "#/components/schemas/DateRuleIO" }, "tags": { "items": { "$ref": "#/components/schemas/TagIO" }, "maxItems": 20, "title": "Tags", "type": "array" } }, "required": [ "name", "emoji", "rule" ], "title": "SeasonIn", "type": "object" }, "SeasonOut": { "additionalProperties": true, "description": "A season a row can follow. A built-in's sources live in code and are not listed.", "properties": { "after_days": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "After Days" }, "builtin": { "title": "Builtin", "type": "boolean" }, "collections": { "items": { "$ref": "#/components/schemas/CollectionIO" }, "maxItems": 10, "title": "Collections", "type": "array" }, "description": { "title": "Description", "type": "string" }, "emoji": { "title": "Emoji", "type": "string" }, "excluded_genres": { "items": { "type": "integer" }, "maxItems": 5, "title": "Excluded Genres", "type": "array" }, "genre": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Genre" }, "lead_days": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Lead Days" }, "name": { "title": "Name", "type": "string" }, "next_dates": { "items": { "type": "string" }, "title": "Next Dates", "type": "array" }, "next_windows": { "items": { "$ref": "#/components/schemas/shortlist__server__api__seasons__SeasonWindowOut" }, "title": "Next Windows", "type": "array" }, "picks": { "items": { "$ref": "#/components/schemas/PickIO" }, "maxItems": 200, "title": "Picks", "type": "array" }, "preset": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preset" }, "rule": { "$ref": "#/components/schemas/DateRuleIO" }, "rule_label": { "title": "Rule Label", "type": "string" }, "slug": { "title": "Slug", "type": "string" }, "tags": { "items": { "$ref": "#/components/schemas/TagIO" }, "maxItems": 20, "title": "Tags", "type": "array" }, "used_by": { "items": { "$ref": "#/components/schemas/UsedByOut" }, "title": "Used By", "type": "array" } }, "required": [ "slug", "name", "emoji", "description", "builtin", "rule", "rule_label", "next_dates", "lead_days", "after_days", "preset", "used_by" ], "title": "SeasonOut", "type": "object" }, "SeasonPreviewIn": { "additionalProperties": true, "description": "A draft season to count, for the row the editor was opened from. Undeclared fields are let through, not\nrefused: the editor posts its whole draft, and a preview stores nothing that a misspelt field could\nsilently fail to set.", "properties": { "collections": { "items": { "$ref": "#/components/schemas/CollectionIO" }, "maxItems": 10, "title": "Collections", "type": "array" }, "excluded_genres": { "items": { "type": "integer" }, "maxItems": 5, "title": "Excluded Genres", "type": "array" }, "genre": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Genre" }, "library_keys": { "items": { "type": "string" }, "title": "Library Keys", "type": "array" }, "media": { "default": "both", "enum": [ "movie", "show", "both" ], "title": "Media", "type": "string" }, "picks": { "items": { "$ref": "#/components/schemas/PickIO" }, "maxItems": 200, "title": "Picks", "type": "array" }, "rule": { "$ref": "#/components/schemas/DateRuleIO" }, "tags": { "items": { "$ref": "#/components/schemas/TagIO" }, "maxItems": 20, "title": "Tags", "type": "array" } }, "required": [ "rule" ], "title": "SeasonPreviewIn", "type": "object" }, "SeasonPreviewOut": { "additionalProperties": true, "description": "What the editor's summary shows. Every count is of titles in the libraries.", "properties": { "from_collections": { "title": "From Collections", "type": "integer" }, "from_genre": { "title": "From Genre", "type": "integer" }, "from_picks": { "title": "From Picks", "type": "integer" }, "from_tags": { "title": "From Tags", "type": "integer" }, "movies": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Movies" }, "next_date": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Next Date" }, "per_collection": { "items": { "$ref": "#/components/schemas/CollectionCountOut" }, "title": "Per Collection", "type": "array" }, "per_tag": { "additionalProperties": { "type": "integer" }, "title": "Per Tag", "type": "object" }, "rule_error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Rule Error" }, "sample": { "items": { "type": "string" }, "title": "Sample", "type": "array" }, "shows": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Shows" }, "total": { "title": "Total", "type": "integer" } }, "required": [ "next_date", "rule_error", "total", "movies", "shows", "from_tags", "from_genre", "from_collections", "from_picks", "per_tag", "per_collection", "sample" ], "title": "SeasonPreviewOut", "type": "object" }, "SeasonStatusOut": { "additionalProperties": true, "description": "Where a seasonal row is in its calendar today, judged on the SERVER's clock.", "properties": { "next": { "anyOf": [ { "$ref": "#/components/schemas/shortlist__server__api__collections__SeasonWindowOut" }, { "type": "null" } ] }, "showing": { "anyOf": [ { "$ref": "#/components/schemas/shortlist__server__api__collections__SeasonWindowOut" }, { "type": "null" } ] } }, "required": [ "showing", "next" ], "title": "SeasonStatusOut", "type": "object" }, "SeenRelease": { "properties": { "version": { "title": "Version", "type": "string" } }, "required": [ "version" ], "title": "SeenRelease", "type": "object" }, "SeerrOptionsOut": { "additionalProperties": true, "properties": { "default_user_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Default User Id" }, "users": { "items": { "$ref": "#/components/schemas/SeerrUserOut" }, "title": "Users", "type": "array" } }, "required": [ "users" ], "title": "SeerrOptionsOut", "type": "object" }, "SeerrUserOut": { "additionalProperties": true, "properties": { "auto_approve_movies": { "default": false, "title": "Auto Approve Movies", "type": "boolean" }, "auto_approve_tv": { "default": false, "title": "Auto Approve Tv", "type": "boolean" }, "id": { "title": "Id", "type": "integer" }, "is_plex_user": { "default": false, "title": "Is Plex User", "type": "boolean" }, "name": { "title": "Name", "type": "string" }, "plex_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Plex Id" } }, "required": [ "id", "name" ], "title": "SeerrUserOut", "type": "object" }, "SendOut": { "additionalProperties": true, "properties": { "dry_run": { "title": "Dry Run", "type": "boolean" }, "outcomes": { "items": { "$ref": "#/components/schemas/SendOutcomeOut" }, "title": "Outcomes", "type": "array" }, "sent": { "title": "Sent", "type": "integer" } }, "required": [ "sent", "dry_run", "outcomes" ], "title": "SendOut", "type": "object" }, "SendOutcomeOut": { "additionalProperties": true, "description": "What the Arr said about one title. `status` is the engine's outcome \u2014 \"requested\",\n\"would_request\" on a dry run, or a skip/error reason the owner can act on.", "properties": { "detail": { "title": "Detail", "type": "string" }, "id": { "title": "Id", "type": "integer" }, "status": { "title": "Status", "type": "string" }, "title": { "title": "Title", "type": "string" } }, "required": [ "id", "title", "status", "detail" ], "title": "SendOutcomeOut", "type": "object" }, "ServerConnectionOut": { "additionalProperties": true, "description": "One advertised address, with the answer to \"does it work from inside the container?\".", "properties": { "local": { "title": "Local", "type": "boolean" }, "ok": { "title": "Ok", "type": "boolean" }, "relay": { "title": "Relay", "type": "boolean" }, "uri": { "title": "Uri", "type": "string" } }, "required": [ "uri", "local", "relay", "ok" ], "title": "ServerConnectionOut", "type": "object" }, "SessionOut": { "additionalProperties": true, "description": "Who the caller is, and whether this instance demands a sign-in at all.\n\nThe signed cookie's contents are spread into the response, so `account_id`/`username` are\npresent only once signed in \u2014 they are declared optional rather than left to `extra`, so the\nSPA gets real types for the two fields it actually reads.", "properties": { "account_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Account Id" }, "authenticated": { "title": "Authenticated", "type": "boolean" }, "login_required": { "title": "Login Required", "type": "boolean" }, "username": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Username" } }, "required": [ "authenticated", "login_required" ], "title": "SessionOut", "type": "object" }, "SettingDefinitionOut": { "additionalProperties": true, "description": "Document every current setting field while preserving nested additions.", "properties": { "assistant_writable": { "default": true, "title": "Assistant Writable", "type": "boolean" }, "default": { "$ref": "#/components/schemas/JsonValue" }, "description": { "title": "Description", "type": "string" }, "effects": { "items": { "$ref": "#/components/schemas/EffectReferenceOut" }, "title": "Effects", "type": "array" }, "group": { "$ref": "#/components/schemas/SettingGroup" }, "has_default": { "title": "Has Default", "type": "boolean" }, "key": { "title": "Key", "type": "string" }, "label": { "title": "Label", "type": "string" }, "nullable": { "default": false, "title": "Nullable", "type": "boolean" }, "options": { "default": [], "items": { "$ref": "#/components/schemas/SettingOptionOut" }, "title": "Options", "type": "array" }, "prerequisites": { "default": [], "items": { "$ref": "#/components/schemas/SettingPrerequisiteOut" }, "title": "Prerequisites", "type": "array" }, "range": { "anyOf": [ { "$ref": "#/components/schemas/NumericRangeOut" }, { "type": "null" } ] }, "required_capabilities": { "items": { "$ref": "#/components/schemas/Capability" }, "title": "Required Capabilities", "type": "array" }, "reset_behavior": { "$ref": "#/components/schemas/ResetBehavior", "default": "restore_default" }, "secret": { "default": false, "title": "Secret", "type": "boolean" }, "value_type": { "$ref": "#/components/schemas/ValueType" } }, "required": [ "key", "label", "description", "value_type", "group", "has_default", "effects", "required_capabilities" ], "title": "SettingDefinitionOut", "type": "object" }, "SettingGroup": { "description": "Stable groups used by settings discovery and grant constraints.", "enum": [ "plex", "metadata", "recommendations", "row_defaults", "requests", "notifications", "schedules", "system", "setup" ], "title": "SettingGroup", "type": "string" }, "SettingOptionOut": { "additionalProperties": true, "description": "Public named option.", "properties": { "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Description" }, "label": { "title": "Label", "type": "string" }, "value": { "$ref": "#/components/schemas/JsonValue" } }, "required": [ "value", "label" ], "title": "SettingOptionOut", "type": "object" }, "SettingPrerequisiteOut": { "additionalProperties": true, "description": "Public applicability condition.", "properties": { "description": { "title": "Description", "type": "string" }, "key": { "title": "Key", "type": "string" }, "values": { "items": { "$ref": "#/components/schemas/JsonValue" }, "title": "Values", "type": "array" } }, "required": [ "key", "values", "description" ], "title": "SettingPrerequisiteOut", "type": "object" }, "SettingsOut": { "additionalProperties": true, "description": "The whole settings store, flat: `{\"row.size\": 15, \"plex.url\": \"\u2026\", \u2026}`.\n\nDeliberately declares NO fields. The key set is genuinely dynamic \u2014 `settings_store.DEFAULTS`\nplus whatever rows the database holds, minus `PRIVATE_KEYS` \u2014 so enumerating it here would be a\nsecond copy of `DEFAULTS` that silently goes stale, and a strict model would DROP every key it\nhad not caught up with. ``extra=\"allow\"`` passes all of them through untouched, which is the\nhonest description of this endpoint: an open map, with secrets already redacted to \"\u2022\u2022\u2022\u2022\u2022\" by\n`all_public()`.", "properties": {}, "title": "SettingsOut", "type": "object" }, "SettingsUpdate": { "properties": { "values": { "additionalProperties": true, "title": "Values", "type": "object" } }, "required": [ "values" ], "title": "SettingsUpdate", "type": "object" }, "SnapshotOut": { "additionalProperties": true, "description": "One un-restored snapshot \u2014 an undo that is still available.", "properties": { "complete": { "title": "Complete", "type": "boolean" }, "entries": { "title": "Entries", "type": "integer" }, "id": { "title": "Id", "type": "integer" }, "taken_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Taken At" }, "user_id": { "title": "User Id", "type": "integer" }, "username": { "title": "Username", "type": "string" } }, "required": [ "id", "user_id", "username", "taken_at", "entries", "complete" ], "title": "SnapshotOut", "type": "object" }, "StatusOut": { "additionalProperties": true, "properties": { "enabled": { "title": "Enabled", "type": "boolean" }, "expires_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Expires At" }, "seconds_remaining": { "title": "Seconds Remaining", "type": "integer" } }, "required": [ "enabled", "expires_at", "seconds_remaining" ], "title": "StatusOut", "type": "object" }, "SyncFinishedEvent": { "additionalProperties": true, "description": "Event ``sync.finished`` \u2014 a Tools-page sync ended.\n\nThe two syncs report different things because they did different work, so most fields belong to\none of them: `count` to the watched sync, `added`/`updated`/`total` to the users sync.", "properties": { "added": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Added" }, "count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Count" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "kind": { "enum": [ "watched", "users", "credited" ], "title": "Kind", "type": "string" }, "ok": { "title": "Ok", "type": "boolean" }, "total": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Total" }, "updated": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Updated" } }, "required": [ "kind", "ok" ], "title": "SyncFinishedEvent", "type": "object" }, "SyncProgressEvent": { "additionalProperties": true, "description": "Event ``sync.progress`` \u2014 a Tools-page sync moved.\n\nThe watched sync is one determinate `done`/`total` loop over users. The users sync has two\nphases: an indeterminate `fetch` (one opaque plex.tv + Tautulli round-trip), then a determinate\n`save` bar over the roster upsert.", "properties": { "done": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Done" }, "kind": { "enum": [ "watched", "users", "credited" ], "title": "Kind", "type": "string" }, "phase": { "anyOf": [ { "enum": [ "fetch", "save" ], "type": "string" }, { "type": "null" } ], "title": "Phase" }, "total": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Total" } }, "required": [ "kind" ], "title": "SyncProgressEvent", "type": "object" }, "SyncStartedOut": { "additionalProperties": true, "properties": { "job_id": { "title": "Job Id", "type": "integer" }, "started": { "title": "Started", "type": "boolean" } }, "required": [ "started", "job_id" ], "title": "SyncStartedOut", "type": "object" }, "TagIO": { "additionalProperties": true, "description": "A TMDB tag (keyword), with its name so the editor can show it without asking TMDB.", "properties": { "id": { "title": "Id", "type": "integer" }, "name": { "title": "Name", "type": "string" } }, "required": [ "id", "name" ], "title": "TagIO", "type": "object" }, "TagMatchOut": { "additionalProperties": true, "description": "How one requester tag on Radarr/Sonarr resolved \u2014 the preview under \"Use my own tags\".", "properties": { "ambiguous": { "title": "Ambiguous", "type": "boolean" }, "display_name": { "title": "Display Name", "type": "string" }, "label": { "title": "Label", "type": "string" }, "source": { "enum": [ "overseerr", "pattern", "override" ], "title": "Source", "type": "string" }, "titles": { "title": "Titles", "type": "integer" }, "user_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "User Id" } }, "required": [ "label", "source", "user_id", "display_name", "titles", "ambiguous" ], "title": "TagMatchOut", "type": "object" }, "TagOut": { "additionalProperties": true, "properties": { "id": { "title": "Id", "type": "integer" }, "movies": { "title": "Movies", "type": "integer" }, "name": { "title": "Name", "type": "string" } }, "required": [ "id", "name", "movies" ], "title": "TagOut", "type": "object" }, "ThemeDiffOut": { "additionalProperties": true, "properties": { "added": { "items": { "type": "string" }, "title": "Added", "type": "array" }, "added_count": { "title": "Added Count", "type": "integer" }, "after_count": { "title": "After Count", "type": "integer" }, "before_count": { "title": "Before Count", "type": "integer" }, "genres_added": { "items": { "type": "string" }, "title": "Genres Added", "type": "array" }, "genres_removed": { "items": { "type": "string" }, "title": "Genres Removed", "type": "array" }, "removed": { "items": { "type": "string" }, "title": "Removed", "type": "array" }, "removed_count": { "title": "Removed Count", "type": "integer" }, "rules_changed": { "title": "Rules Changed", "type": "boolean" }, "tags_added": { "items": { "type": "string" }, "title": "Tags Added", "type": "array" }, "tags_removed": { "items": { "type": "string" }, "title": "Tags Removed", "type": "array" }, "unchanged": { "items": { "type": "string" }, "title": "Unchanged", "type": "array" } }, "required": [ "rules_changed", "added", "removed", "unchanged", "added_count", "removed_count", "tags_added", "tags_removed", "genres_added", "genres_removed", "before_count", "after_count" ], "title": "ThemeDiffOut", "type": "object" }, "ThemeIn": { "description": "A theme to store. Undeclared fields (a hash echoed back from a preview) are ignored, never stored.", "properties": { "brief": { "default": "", "maxLength": 1000, "title": "Brief", "type": "string" }, "collections": { "items": { "$ref": "#/components/schemas/CollectionIO" }, "maxItems": 10, "title": "Collections", "type": "array" }, "emoji": { "anyOf": [ { "maxLength": 8, "type": "string" }, { "type": "null" } ], "title": "Emoji" }, "excluded_genres": { "items": { "type": "string" }, "maxItems": 10, "title": "Excluded Genres", "type": "array" }, "genres": { "items": { "type": "string" }, "maxItems": 10, "title": "Genres", "type": "array" }, "media": { "items": { "enum": [ "movie", "show" ], "type": "string" }, "maxItems": 2, "minItems": 1, "title": "Media", "type": "array" }, "name": { "maxLength": 60, "minLength": 1, "title": "Name", "type": "string" }, "origin": { "default": "manual", "enum": [ "ai", "manual" ], "title": "Origin", "type": "string" }, "picks": { "items": { "$ref": "#/components/schemas/ThemePickIO" }, "maxItems": 200, "title": "Picks", "type": "array" }, "rules": { "$ref": "#/components/schemas/RulesIO" }, "tags": { "items": { "$ref": "#/components/schemas/TagIO" }, "maxItems": 20, "title": "Tags", "type": "array" } }, "required": [ "name", "media" ], "title": "ThemeIn", "type": "object" }, "ThemeOut": { "additionalProperties": true, "properties": { "ai_tokens": { "title": "Ai Tokens", "type": "integer" }, "brief": { "title": "Brief", "type": "string" }, "collections": { "items": { "$ref": "#/components/schemas/CollectionIO" }, "title": "Collections", "type": "array" }, "content_hash": { "title": "Content Hash", "type": "string" }, "emoji": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Emoji" }, "excluded_genres": { "items": { "type": "string" }, "title": "Excluded Genres", "type": "array" }, "genres": { "items": { "type": "string" }, "title": "Genres", "type": "array" }, "id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Id" }, "media": { "items": { "type": "string" }, "title": "Media", "type": "array" }, "name": { "title": "Name", "type": "string" }, "origin": { "title": "Origin", "type": "string" }, "picks": { "items": { "$ref": "#/components/schemas/ThemePickIO" }, "title": "Picks", "type": "array" }, "rules": { "$ref": "#/components/schemas/RulesIO" }, "slug": { "title": "Slug", "type": "string" }, "stats": { "additionalProperties": { "type": "integer" }, "title": "Stats", "type": "object" }, "tags": { "items": { "$ref": "#/components/schemas/TagIO" }, "title": "Tags", "type": "array" }, "topped_up_at": { "anyOf": [ { "format": "date-time", "type": "string" }, { "type": "null" } ], "title": "Topped Up At" } }, "required": [ "id", "slug", "name", "emoji", "brief", "origin", "media", "tags", "genres", "excluded_genres", "collections", "picks", "rules", "content_hash", "ai_tokens", "stats" ], "title": "ThemeOut", "type": "object" }, "ThemePickIO": { "additionalProperties": true, "description": "A title a theme names by TMDB id; ``origin`` says whether the AI or the owner chose it.", "properties": { "media": { "enum": [ "movie", "show" ], "title": "Media", "type": "string" }, "origin": { "default": "owner", "enum": [ "ai", "owner" ], "title": "Origin", "type": "string" }, "reason": { "anyOf": [ { "maxLength": 160, "type": "string" }, { "type": "null" } ], "title": "Reason" }, "title": { "default": "", "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "tmdb_id", "media" ], "title": "ThemePickIO", "type": "object" }, "ThemeRefOut": { "additionalProperties": true, "description": "A theme as one person's rotation holds it: which one, when it started, and when it hands over.", "properties": { "due_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Due At" }, "emoji": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Emoji" }, "name": { "title": "Name", "type": "string" }, "started_at": { "title": "Started At", "type": "string" }, "theme_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "description": "The stored theme; null once it has been deleted.", "title": "Theme Id" } }, "required": [ "theme_id", "name", "emoji", "started_at", "due_at" ], "title": "ThemeRefOut", "type": "object" }, "ThemeRotationOut": { "additionalProperties": true, "properties": { "days": { "description": "How many days a theme lasts.", "title": "Days", "type": "integer" }, "mode": { "enum": [ "fixed", "explore" ], "title": "Mode", "type": "string" }, "targets": { "items": { "$ref": "#/components/schemas/RotationTargetOut" }, "title": "Targets", "type": "array" } }, "required": [ "mode", "days", "targets" ], "title": "ThemeRotationOut", "type": "object" }, "ThemeSaveIn": { "description": "A save: the theme, what the AI call that wrote it cost, and the row it was written for.", "properties": { "collection_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Collection Id" }, "draft": { "$ref": "#/components/schemas/ThemeIn" }, "stats": { "additionalProperties": { "type": "integer" }, "title": "Stats", "type": "object" }, "tokens": { "default": 0, "minimum": 0.0, "title": "Tokens", "type": "integer" } }, "required": [ "draft" ], "title": "ThemeSaveIn", "type": "object" }, "ThemeStatsOut": { "additionalProperties": true, "properties": { "after_rules": { "title": "After Rules", "type": "integer" }, "ai_kept": { "default": 0, "title": "Ai Kept", "type": "integer" }, "in_library": { "title": "In Library", "type": "integer" }, "named": { "title": "Named", "type": "integer" }, "resolved": { "title": "Resolved", "type": "integer" }, "runtime_checked": { "default": 0, "title": "Runtime Checked", "type": "integer" }, "runtime_total": { "default": 0, "title": "Runtime Total", "type": "integer" }, "truncated": { "default": false, "title": "Truncated", "type": "boolean" }, "unwatched_median": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Unwatched Median" } }, "required": [ "named", "resolved", "in_library", "after_rules", "unwatched_median" ], "title": "ThemeStatsOut", "type": "object" }, "TitleMatchOut": { "additionalProperties": true, "description": "TMDB's own best guess for a title search, for the \"block a seed\" picker.", "properties": { "media_type": { "title": "Media Type", "type": "string" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Tmdb Id" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "tmdb_id", "title", "media_type", "year" ], "title": "TitleMatchOut", "type": "object" }, "TopTitleOut": { "additionalProperties": true, "properties": { "media_type": { "title": "Media Type", "type": "string" }, "rating_key": { "title": "Rating Key", "type": "integer" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "watcher_sample": { "items": { "$ref": "#/components/schemas/WatcherOut" }, "title": "Watcher Sample", "type": "array" }, "watchers": { "title": "Watchers", "type": "integer" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "tmdb_id", "media_type", "title", "watchers", "rating_key", "year", "watcher_sample" ], "title": "TopTitleOut", "type": "object" }, "TraceRequestOut": { "additionalProperties": true, "description": "What became of one wanted-but-missing title, keyed `\":\"` on the trace.", "properties": { "arr_slug": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Arr Slug" }, "detail": { "title": "Detail", "type": "string" }, "excluded": { "title": "Excluded", "type": "boolean" }, "status": { "enum": [ "pending", "sent", "rejected" ], "title": "Status", "type": "string" } }, "required": [ "status", "detail", "arr_slug", "excluded" ], "title": "TraceRequestOut", "type": "object" }, "TransferIn": { "properties": { "dry_run": { "default": false, "title": "Dry Run", "type": "boolean" }, "from_user_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "From User Id" }, "to_user_id": { "title": "To User Id", "type": "integer" } }, "required": [ "to_user_id" ], "title": "TransferIn", "type": "object" }, "TransferOut": { "additionalProperties": true, "description": "The result of one replication.\n\n`unmarks` and `offsets_cleared` are the destructive half and are reported separately from\n`applied` on purpose: \"wrote 11,000 things\" and \"removed 412 watches from that account\" are not\nthe same sentence, and only the second needs anybody's consent.", "properties": { "applied": { "title": "Applied", "type": "integer" }, "dry_run": { "title": "Dry Run", "type": "boolean" }, "errors": { "items": { "type": "string" }, "title": "Errors", "type": "array" }, "events_copied": { "title": "Events Copied", "type": "integer" }, "failed": { "title": "Failed", "type": "integer" }, "marks": { "title": "Marks", "type": "integer" }, "offsets_cleared": { "title": "Offsets Cleared", "type": "integer" }, "offsets_set": { "title": "Offsets Set", "type": "integer" }, "planned": { "title": "Planned", "type": "integer" }, "removals_preview": { "items": { "type": "string" }, "title": "Removals Preview", "type": "array" }, "shows_cleared": { "title": "Shows Cleared", "type": "integer" }, "snapshot_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Snapshot Id" }, "source_empty": { "title": "Source Empty", "type": "boolean" }, "target_unreadable": { "items": { "type": "string" }, "title": "Target Unreadable", "type": "array" }, "titles_cached": { "title": "Titles Cached", "type": "integer" }, "unmarks": { "title": "Unmarks", "type": "integer" }, "unreachable": { "title": "Unreachable", "type": "integer" }, "verify_checked": { "title": "Verify Checked", "type": "integer" }, "verify_mismatched": { "title": "Verify Mismatched", "type": "integer" } }, "required": [ "planned", "applied", "unreachable", "failed", "marks", "unmarks", "offsets_set", "offsets_cleared", "removals_preview", "verify_mismatched", "verify_checked", "shows_cleared", "target_unreadable", "events_copied", "titles_cached", "snapshot_id", "dry_run", "source_empty", "errors" ], "title": "TransferOut", "type": "object" }, "TrendPointOut": { "additionalProperties": true, "properties": { "finished": { "title": "Finished", "type": "integer" }, "watched": { "title": "Watched", "type": "integer" }, "week": { "title": "Week", "type": "string" } }, "required": [ "week", "watched", "finished" ], "title": "TrendPointOut", "type": "object" }, "UndoIn": { "properties": { "dry_run": { "default": false, "title": "Dry Run", "type": "boolean" }, "snapshot_id": { "title": "Snapshot Id", "type": "integer" } }, "required": [ "snapshot_id" ], "title": "UndoIn", "type": "object" }, "UninstallCollectionOut": { "additionalProperties": true, "description": "One Shortlist collection the uninstall deletes, by where it lives and whose it is.", "properties": { "library": { "title": "Library", "type": "string" }, "person": { "title": "Person", "type": "string" }, "title": { "title": "Title", "type": "string" } }, "required": [ "library", "person", "title" ], "title": "UninstallCollectionOut", "type": "object" }, "UninstallFailedOut": { "additionalProperties": true, "description": "An account plex.tv refused. The rest of the uninstall still ran (issue #96).", "properties": { "error": { "title": "Error", "type": "string" }, "user": { "title": "User", "type": "string" } }, "required": [ "user", "error" ], "title": "UninstallFailedOut", "type": "object" }, "UninstallOut": { "additionalProperties": true, "properties": { "collections_deleted": { "items": { "type": "string" }, "title": "Collections Deleted", "type": "array" }, "collections_detail": { "items": { "$ref": "#/components/schemas/UninstallCollectionOut" }, "title": "Collections Detail", "type": "array" }, "dry_run": { "title": "Dry Run", "type": "boolean" }, "filters_failed": { "items": { "$ref": "#/components/schemas/UninstallFailedOut" }, "title": "Filters Failed", "type": "array" }, "filters_restored": { "title": "Filters Restored", "type": "integer" }, "filters_skipped": { "items": { "$ref": "#/components/schemas/UninstallSkippedOut" }, "title": "Filters Skipped", "type": "array" }, "filters_unreachable": { "items": { "$ref": "#/components/schemas/UninstallUnreachableOut" }, "title": "Filters Unreachable", "type": "array" }, "message": { "title": "Message", "type": "string" }, "rows_disabled": { "title": "Rows Disabled", "type": "integer" } }, "required": [ "filters_restored", "filters_skipped", "filters_unreachable", "filters_failed", "collections_deleted", "rows_disabled", "dry_run", "message" ], "title": "UninstallOut", "type": "object" }, "UninstallProgressEvent": { "additionalProperties": true, "description": "Event ``uninstall.progress`` \u2014 one live step of a REAL uninstall (the dry-run preview is\ninstant and streams nothing).", "properties": { "done": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Done" }, "label": { "title": "Label", "type": "string" }, "total": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Total" } }, "required": [ "label" ], "title": "UninstallProgressEvent", "type": "object" }, "UninstallRequest": { "properties": { "confirm": { "default": "", "title": "Confirm", "type": "string" }, "dry_run": { "default": false, "title": "Dry Run", "type": "boolean" } }, "title": "UninstallRequest", "type": "object" }, "UninstallSkippedOut": { "additionalProperties": true, "description": "An account that has left this Plex server, so its snapshot can never be restored.", "properties": { "plex_account_id": { "title": "Plex Account Id", "type": "integer" }, "reason": { "title": "Reason", "type": "string" }, "user": { "title": "User", "type": "string" } }, "required": [ "user", "plex_account_id", "reason" ], "title": "UninstallSkippedOut", "type": "object" }, "UninstallUnreachableOut": { "additionalProperties": true, "description": "An account plex.tv's roster did not list, but that our own records say IS on this server.\n\nIts own field rather than folded into `filters_failed`, because the two need different things\nfrom the operator \u2014 a refused write is worth retrying, an absent roster entry means plex.tv gave\nan answer we don't believe \u2014 and a consumer should never have to match on an error string to\ntell them apart.", "properties": { "plex_account_id": { "title": "Plex Account Id", "type": "integer" }, "reason": { "title": "Reason", "type": "string" }, "user": { "title": "User", "type": "string" } }, "required": [ "user", "plex_account_id", "reason" ], "title": "UninstallUnreachableOut", "type": "object" }, "UpNextRequest": { "additionalProperties": false, "properties": { "theme_id": { "title": "Theme Id", "type": "integer" }, "user_id": { "title": "User Id", "type": "integer" } }, "required": [ "user_id", "theme_id" ], "title": "UpNextRequest", "type": "object" }, "UsedByOut": { "additionalProperties": true, "description": "A row that follows the season.", "properties": { "id": { "title": "Id", "type": "integer" }, "name": { "title": "Name", "type": "string" } }, "required": [ "id", "name" ], "title": "UsedByOut", "type": "object" }, "UserOut": { "additionalProperties": true, "description": "One person \u2014 the response shape of :func:`user_dict`.", "properties": { "avatar_url": { "title": "Avatar Url", "type": "string" }, "cold_start": { "title": "Cold Start", "type": "boolean" }, "departed": { "title": "Departed", "type": "boolean" }, "display_name": { "title": "Display Name", "type": "string" }, "enabled": { "title": "Enabled", "type": "boolean" }, "friendly_name": { "title": "Friendly Name", "type": "string" }, "history_depth": { "title": "History Depth", "type": "integer" }, "id": { "title": "Id", "type": "integer" }, "last_pick_watched_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Pick Watched At" }, "last_run_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Run At" }, "manage_sharing": { "title": "Manage Sharing", "type": "boolean" }, "nickname": { "title": "Nickname", "type": "string" }, "picks_watched_30d": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Picks Watched 30D" }, "plex_account_id": { "title": "Plex Account Id", "type": "integer" }, "prefs": { "additionalProperties": true, "title": "Prefs", "type": "object" }, "preview_titles": { "items": { "type": "string" }, "title": "Preview Titles", "type": "array" }, "request_tag": { "title": "Request Tag", "type": "string" }, "requested_by_tag": { "title": "Requested By Tag", "type": "string" }, "restricted": { "title": "Restricted", "type": "boolean" }, "restriction_profile": { "title": "Restriction Profile", "type": "string" }, "seerr_user_id": { "title": "Seerr User Id", "type": "integer" }, "slug": { "title": "Slug", "type": "string" }, "unhidden_rows": { "title": "Unhidden Rows", "type": "integer" }, "user_type": { "$ref": "#/components/schemas/UserType" }, "username": { "title": "Username", "type": "string" } }, "required": [ "id", "plex_account_id", "username", "slug", "nickname", "friendly_name", "display_name", "avatar_url", "user_type", "restricted", "restriction_profile", "enabled", "manage_sharing", "cold_start", "request_tag", "requested_by_tag", "seerr_user_id", "prefs", "history_depth", "last_run_at", "picks_watched_30d", "last_pick_watched_at", "preview_titles", "unhidden_rows", "departed" ], "title": "UserOut", "type": "object" }, "UserPatch": { "properties": { "enabled": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Enabled" }, "manage_sharing": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Manage Sharing" }, "nickname": { "anyOf": [ { "maxLength": 255, "type": "string" }, { "type": "null" } ], "title": "Nickname" }, "prefs": { "anyOf": [ { "$ref": "#/components/schemas/UserPrefs" }, { "type": "null" } ] }, "request_tag": { "anyOf": [ { "maxLength": 64, "type": "string" }, { "type": "null" } ], "title": "Request Tag" }, "requested_by_tag": { "anyOf": [ { "maxLength": 64, "type": "string" }, { "type": "null" } ], "title": "Requested By Tag" }, "seerr_user_id": { "anyOf": [ { "minimum": 0.0, "type": "integer" }, { "type": "null" } ], "title": "Seerr User Id" } }, "title": "UserPatch", "type": "object" }, "UserPickOut": { "additionalProperties": true, "description": "One delivered pick as the USER pages render it \u2014 the response shape of :func:`pick_dict`.\n\nNot ``PickOut``: the run detail publishes its own, narrower pick shape under that name\n(``api/schemas_runs.py``), and two models sharing a name make FastAPI fall back to\nfully-qualified schema names for both \u2014 which the SPA then generates as\n``shortlist__server__api__serializers__PickOut``.", "properties": { "affinity": { "title": "Affinity", "type": "number" }, "collection_slug": { "title": "Collection Slug", "type": "string" }, "library": { "title": "Library", "type": "string" }, "media_type": { "title": "Media Type", "type": "string" }, "rank": { "title": "Rank", "type": "integer" }, "rating": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Rating" }, "rating_key": { "title": "Rating Key", "type": "integer" }, "reason": { "title": "Reason", "type": "string" }, "section_key": { "title": "Section Key", "type": "string" }, "seed_title": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Seed Title" }, "sources": { "items": { "type": "string" }, "title": "Sources", "type": "array" }, "title": { "title": "Title", "type": "string" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "rank", "title", "reason", "rating_key", "media_type", "collection_slug", "library", "section_key", "seed_title", "sources", "affinity", "year", "rating" ], "title": "UserPickOut", "type": "object" }, "UserPickOutcomeOut": { "additionalProperties": true, "description": "One title this person was recommended and then played.", "properties": { "finished_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Finished At" }, "media_type": { "title": "Media Type", "type": "string" }, "outcome": { "title": "Outcome", "type": "string" }, "percent": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Percent" }, "row": { "title": "Row", "type": "string" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "title": "Tmdb Id", "type": "integer" }, "watched_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Watched At" } }, "required": [ "tmdb_id", "media_type", "title", "row", "outcome", "percent", "watched_at", "finished_at" ], "title": "UserPickOutcomeOut", "type": "object" }, "UserPrefs": { "properties": { "blocked_seeds": { "anyOf": [ { "items": { "anyOf": [ { "type": "integer" }, { "$ref": "#/components/schemas/BlockSeedBody" } ] }, "type": "array" }, { "type": "null" } ], "title": "Blocked Seeds" }, "excluded_genres": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "title": "Excluded Genres" }, "paused": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Paused" }, "row_name_tpl": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Row Name Tpl" } }, "title": "UserPrefs", "type": "object" }, "UserRowOut": { "additionalProperties": true, "description": "One row this person gets, in one library: its effective settings, their override, its picks.", "properties": { "collection_id": { "title": "Collection Id", "type": "integer" }, "favourite_count": { "title": "Favourite Count", "type": "integer" }, "is_default": { "title": "Is Default", "type": "boolean" }, "library": { "title": "Library", "type": "string" }, "media": { "title": "Media", "type": "string" }, "muted": { "title": "Muted", "type": "boolean" }, "name": { "title": "Name", "type": "string" }, "older_count": { "title": "Older Count", "type": "integer" }, "override": { "$ref": "#/components/schemas/RowOverrideOut" }, "picks": { "items": { "$ref": "#/components/schemas/UserPickOut" }, "title": "Picks", "type": "array" }, "recent_count": { "title": "Recent Count", "type": "integer" }, "section_key": { "title": "Section Key", "type": "string" }, "size": { "title": "Size", "type": "integer" }, "slug": { "title": "Slug", "type": "string" } }, "required": [ "collection_id", "slug", "name", "media", "library", "section_key", "size", "recent_count", "favourite_count", "older_count", "is_default", "muted", "override", "picks" ], "title": "UserRowOut", "type": "object" }, "UserRunOut": { "additionalProperties": true, "description": "One run as it went for ONE person: their outcome, not the run's.", "properties": { "diff": { "additionalProperties": true, "title": "Diff", "type": "object" }, "dry_run": { "title": "Dry Run", "type": "boolean" }, "duration_ms": { "title": "Duration Ms", "type": "integer" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error" }, "finished_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Finished At" }, "picks": { "items": { "$ref": "#/components/schemas/UserPickOut" }, "title": "Picks", "type": "array" }, "reason": { "title": "Reason", "type": "string" }, "run_id": { "title": "Run Id", "type": "integer" }, "run_status": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Run Status" }, "started_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Started At" }, "status": { "title": "Status", "type": "string" } }, "required": [ "run_id", "started_at", "finished_at", "status", "error", "reason", "duration_ms", "run_status", "dry_run", "diff", "picks" ], "title": "UserRunOut", "type": "object" }, "UserRunsSummaryOut": { "additionalProperties": true, "properties": { "included": { "title": "Included", "type": "integer" }, "total": { "title": "Total", "type": "integer" } }, "required": [ "included", "total" ], "title": "UserRunsSummaryOut", "type": "object" }, "UserSyncOut": { "additionalProperties": true, "properties": { "added": { "title": "Added", "type": "integer" }, "total": { "title": "Total", "type": "integer" }, "updated": { "title": "Updated", "type": "integer" } }, "required": [ "added", "updated", "total" ], "title": "UserSyncOut", "type": "object" }, "UserType": { "enum": [ "owner", "shared", "managed" ], "title": "UserType", "type": "string" }, "ValidationError": { "properties": { "ctx": { "title": "Context", "type": "object" }, "input": { "title": "Input" }, "loc": { "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location", "type": "array" }, "msg": { "title": "Message", "type": "string" }, "type": { "title": "Error Type", "type": "string" } }, "required": [ "loc", "msg", "type" ], "title": "ValidationError", "type": "object" }, "ValueType": { "description": "JSON value shapes accepted for settings.", "enum": [ "boolean", "integer", "number", "string", "string_list", "integer_list", "object" ], "title": "ValueType", "type": "string" }, "VersionOut": { "additionalProperties": true, "description": "`GET /version`.\n\n``extra=\"allow\"`` is on EVERY response model in this file, nested ones included, and is not\noptional: a strict Pydantic response model silently DROPS any key the handler returned but the\nmodel does not declare. With it, an undeclared key passes through untouched \u2014 the model\ndocuments the shape without filtering it, so a field missed here cannot vanish from the payload\nand blank out a page.", "properties": { "current_version": { "title": "Current Version", "type": "string" }, "git_branch": { "title": "Git Branch", "type": "string" }, "git_sha": { "title": "Git Sha", "type": "string" }, "install_type": { "title": "Install Type", "type": "string" }, "latest_version": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Latest Version" }, "update_available": { "title": "Update Available", "type": "boolean" } }, "required": [ "current_version", "latest_version", "update_available", "install_type", "git_sha", "git_branch" ], "title": "VersionOut", "type": "object" }, "ViewingShareOut": { "additionalProperties": true, "description": "Of the titles people watched in the window, how many their Shortlist row had shown them.", "properties": { "from_rows": { "title": "From Rows", "type": "integer" }, "rate": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Rate" }, "watched": { "title": "Watched", "type": "integer" } }, "required": [ "watched", "from_rows", "rate" ], "title": "ViewingShareOut", "type": "object" }, "WatchItemOut": { "additionalProperties": true, "description": "One recent watch, from the same source recommendations are built from.", "properties": { "episode": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Episode" }, "episode_title": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Episode Title" }, "media_type": { "title": "Media Type", "type": "string" }, "season": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Season" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Tmdb Id" }, "watched_at": { "title": "Watched At", "type": "string" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "title", "tmdb_id", "media_type", "watched_at", "year", "season", "episode", "episode_title" ], "title": "WatchItemOut", "type": "object" }, "WatchSyncOut": { "additionalProperties": true, "properties": { "last": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last" }, "live_down_since": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Live Down Since" }, "live_since": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Live Since" }, "next": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Next" } }, "required": [ "last", "next", "live_since", "live_down_since" ], "title": "WatchSyncOut", "type": "object" }, "WatchedLibraryOut": { "additionalProperties": true, "description": "One Plex library this person has a cached watch in.\n\nThe `media_type` is what lets the page decide whether a library filter is worth showing: one\nlibrary per type means the Movies/Shows buttons already draw every distinction a library choice\ncould, and a second control offering the same two words is noise (#111).", "properties": { "media_type": { "title": "Media Type", "type": "string" }, "name": { "title": "Name", "type": "string" } }, "required": [ "name", "media_type" ], "title": "WatchedLibraryOut", "type": "object" }, "WatchedPageOut": { "additionalProperties": true, "description": "A page of the watched set, plus how complete the set behind it is.\n\nThe staleness fields travel WITH the page on purpose: this list is a cache, and a page that\ndoesn't say when it was last filled invites \"I watched that, why is it recommended?\" \u2014 the exact\nquestion this endpoint exists to answer.", "properties": { "dislike_threshold": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Dislike Threshold" }, "items": { "items": { "$ref": "#/components/schemas/WatchedTitleOut" }, "title": "Items", "type": "array" }, "last_full_sync_at": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Last Full Sync At" }, "libraries": { "items": { "$ref": "#/components/schemas/WatchedLibraryOut" }, "title": "Libraries", "type": "array" }, "rated_count": { "title": "Rated Count", "type": "integer" }, "ratings_trusted": { "title": "Ratings Trusted", "type": "boolean" }, "synced_titles": { "title": "Synced Titles", "type": "integer" }, "total": { "title": "Total", "type": "integer" } }, "required": [ "items", "total", "libraries", "last_full_sync_at", "synced_titles", "dislike_threshold", "ratings_trusted", "rated_count" ], "title": "WatchedPageOut", "type": "object" }, "WatchedTitleOut": { "additionalProperties": true, "description": "One TITLE from the cached watched set \u2014 the set recommendations are actually filtered against.\n\nOne title, not one stored row: a title held in two Plex libraries is cached once per library, and\nthose copies are merged here (issue #111). `libraries` names the ones it was found in, and every\nother field is merged to the claim the engine acts on \u2014 see `_merge_watched_copies`.", "properties": { "leaf_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Leaf Count" }, "libraries": { "items": { "type": "string" }, "title": "Libraries", "type": "array" }, "media_type": { "title": "Media Type", "type": "string" }, "rating_key": { "title": "Rating Key", "type": "integer" }, "title": { "title": "Title", "type": "string" }, "tmdb_id": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Tmdb Id" }, "user_rating": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "User Rating" }, "viewed_leaf_count": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Viewed Leaf Count" }, "watch_count": { "title": "Watch Count", "type": "integer" }, "watched_at": { "title": "Watched At", "type": "string" }, "year": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Year" } }, "required": [ "title", "rating_key", "tmdb_id", "media_type", "watched_at", "year", "watch_count", "libraries", "viewed_leaf_count", "leaf_count", "user_rating" ], "title": "WatchedTitleOut", "type": "object" }, "WatcherOut": { "additionalProperties": true, "properties": { "id": { "title": "Id", "type": "integer" }, "name": { "title": "Name", "type": "string" } }, "required": [ "id", "name" ], "title": "WatcherOut", "type": "object" }, "WebPromptPreviewIn": { "additionalProperties": false, "description": "What to preview: a row's instructions and/or unsaved server-wide text; either may be omitted.", "properties": { "ai_instructions": { "anyOf": [ { "$ref": "#/components/schemas/AiInstructionsIn" }, { "type": "null" } ] }, "server_text": { "anyOf": [ { "maxLength": 2000, "type": "string" }, { "type": "null" } ], "title": "Server Text" } }, "title": "WebPromptPreviewIn", "type": "object" }, "WebPromptPreviewOut": { "additionalProperties": true, "description": "The prompt AI web search would send, the built-in wording, and whether instructions do anything.", "properties": { "backend": { "title": "Backend", "type": "string" }, "builtin_guidance": { "title": "Builtin Guidance", "type": "string" }, "builtin_template": { "title": "Builtin Template", "type": "string" }, "inert": { "title": "Inert", "type": "boolean" }, "system": { "title": "System", "type": "string" } }, "required": [ "backend", "system", "builtin_guidance", "builtin_template", "inert" ], "title": "WebPromptPreviewOut", "type": "object" }, "WebShareOut": { "additionalProperties": true, "description": "The share of a run's delivered picks that the AI web search found (#152). None = no picks to share.", "properties": { "all": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "All" }, "picked": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "title": "Picked" } }, "required": [ "picked", "all" ], "title": "WebShareOut", "type": "object" }, "WhatsNewOut": { "additionalProperties": true, "properties": { "releases": { "items": { "$ref": "#/components/schemas/ReleaseNotesOut" }, "title": "Releases", "type": "array" }, "version": { "title": "Version", "type": "string" } }, "required": [ "version", "releases" ], "title": "WhatsNewOut", "type": "object" }, "WizardState": { "properties": { "completed": { "default": false, "title": "Completed", "type": "boolean" }, "state": { "additionalProperties": true, "default": {}, "title": "State", "type": "object" }, "step": { "title": "Step", "type": "integer" } }, "required": [ "step" ], "title": "WizardState", "type": "object" }, "WizardStateOut": { "additionalProperties": true, "description": "Resumable wizard progress. Every field has a settings-store default (step 0, {}, False), so a\nnever-started instance answers with those rather than nulls.", "properties": { "completed": { "title": "Completed", "type": "boolean" }, "state": { "additionalProperties": true, "title": "State", "type": "object" }, "step": { "title": "Step", "type": "integer" } }, "required": [ "step", "state", "completed" ], "title": "WizardStateOut", "type": "object" }, "WizardStateSavedOut": { "additionalProperties": true, "description": "The receipt for `PUT /state` \u2014 an echo, not a re-read: `state` is deliberately not returned.", "properties": { "completed": { "title": "Completed", "type": "boolean" }, "step": { "title": "Step", "type": "integer" } }, "required": [ "step", "completed" ], "title": "WizardStateSavedOut", "type": "object" }, "shortlist__server__api__collections__SeasonWindowOut": { "additionalProperties": true, "description": "One season's run for a row: which season, and the first and last days the row shows it.", "properties": { "emoji": { "title": "Emoji", "type": "string" }, "ends": { "title": "Ends", "type": "string" }, "name": { "title": "Name", "type": "string" }, "slug": { "title": "Slug", "type": "string" }, "starts": { "title": "Starts", "type": "string" } }, "required": [ "slug", "name", "emoji", "starts", "ends" ], "title": "SeasonWindowOut", "type": "object" }, "shortlist__server__api__seasons__SeasonWindowOut": { "additionalProperties": true, "description": "Inclusive calendar dates for a month-long season.", "properties": { "end": { "title": "End", "type": "string" }, "start": { "title": "Start", "type": "string" } }, "required": [ "start", "end" ], "title": "SeasonWindowOut", "type": "object" } } }, "info": { "title": "Shortlist", "version": "1.15.0" }, "openapi": "3.1.0", "paths": { "/api/ai/web-prompt-preview": { "post": { "description": "The system prompt AI web search sends with these instructions. Reads settings; writes nothing.", "operationId": "web_prompt_preview_api_ai_web_prompt_preview_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebPromptPreviewIn" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebPromptPreviewOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Web Prompt Preview", "tags": [ "ai" ] } }, "/api/assistant/changes/{change_id}": { "get": { "operationId": "review_change_api_assistant_changes__change_id__get", "parameters": [ { "in": "path", "name": "change_id", "required": true, "schema": { "title": "Change Id", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Review Change Api Assistant Changes Change Id Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Review Change", "tags": [ "assistant" ] } }, "/api/assistant/changes/{change_id}/approve": { "post": { "operationId": "approve_change_api_assistant_changes__change_id__approve_post", "parameters": [ { "in": "path", "name": "change_id", "required": true, "schema": { "title": "Change Id", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Approve Change Api Assistant Changes Change Id Approve Post", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Approve Change", "tags": [ "assistant" ] } }, "/api/assistant/status": { "get": { "operationId": "status_api_assistant_status_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Status Api Assistant Status Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Status", "tags": [ "assistant" ] } }, "/api/auth/logout": { "post": { "operationId": "logout_api_auth_logout_post", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LogoutOut" } } }, "description": "Successful Response" } }, "summary": "Logout", "tags": [ "auth" ] } }, "/api/auth/pin": { "post": { "operationId": "create_pin_api_auth_pin_post", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PinOut" } } }, "description": "Successful Response" } }, "summary": "Create Pin", "tags": [ "auth" ] } }, "/api/auth/pin/{pin_id}": { "get": { "description": "Poll the PIN; once linked, verify the account is the server owner and set the session.", "operationId": "poll_pin_api_auth_pin__pin_id__get", "parameters": [ { "in": "path", "name": "pin_id", "required": true, "schema": { "title": "Pin Id", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PinStatusOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Poll Pin", "tags": [ "auth" ] } }, "/api/auth/session": { "get": { "operationId": "get_session_api_auth_session_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SessionOut" } } }, "description": "Successful Response" } }, "summary": "Get Session", "tags": [ "auth" ] } }, "/api/catalogs/settings": { "get": { "operationId": "settings_catalog_api_catalogs_settings_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/SettingDefinitionOut" }, "title": "Response Settings Catalog Api Catalogs Settings Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "Settings Catalog", "tags": [ "catalogs" ] } }, "/api/catalogs/templates": { "get": { "operationId": "template_catalog_api_catalogs_templates_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/RowTemplateDefinitionOut" }, "title": "Response Template Catalog Api Catalogs Templates Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "Template Catalog", "tags": [ "catalogs" ] } }, "/api/collections": { "get": { "operationId": "list_collections_api_collections_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/CollectionOut" }, "title": "Response List Collections Api Collections Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "List Collections", "tags": [ "collections" ] }, "post": { "operationId": "create_collection_api_collections_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionIn" } } }, "required": true }, "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Create Collection", "tags": [ "collections" ] } }, "/api/collections/{collection_id}": { "delete": { "description": "Delete a row, or with ``dry_run=true`` report what deleting it would do and write nothing.\n\nA query parameter rather than a body: `DELETE` bodies are awkward through both `fetch` and\nFastAPI, and this works with the SPA's existing `request()` helper unchanged.\n\n`POST /{id}/cleanup?dry_run=true` already previews the PLEX half. What only this can show is the\nLOCAL half, and one part of it is a genuine surprise: deleting this row silently strips every\nOTHER row's shelf placement that was positioned relative to it, changing where two other people's\nrows appear. That was logged after the fact and warned about nowhere.\n\nA preview answers 200 with :class:`RowDeletePreviewOut` instead of the delete's 204.", "operationId": "delete_collection_api_collections__collection_id__delete", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } }, { "in": "query", "name": "dry_run", "required": false, "schema": { "default": false, "title": "Dry Run", "type": "boolean" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RowDeletePreviewOut" } } }, "description": "A dry-run preview; nothing was deleted." }, "204": { "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Delete Collection", "tags": [ "collections" ] }, "patch": { "description": "Edit a row: validate \u2192 apply \u2192 plan the Plex work \u2192 enqueue it \u2192 drain.\n\nThe decision table for \"what does this edit owe Plex\" lives in `shortlist/server/services/row_changes.py`, not here.\nIt used to be eleven mutable flags accumulated down this handler and eight conditional\ndispatches at the bottom \u2014 untestable without a Plex context, and the place a missed branch\nsilently left someone's row on the wrong Home screen.", "operationId": "update_collection_api_collections__collection_id__patch", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionIn" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Update Collection", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/ai-pause": { "post": { "description": "Pause or resume an AI row's AI. A paused row keeps its theme and keeps building from it; it just never\nspends tokens writing or refining one (409 from the theme endpoints until resumed).", "operationId": "pause_ai_api_collections__collection_id__ai_pause_post", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AiPauseRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Pause Ai", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/cleanup": { "post": { "description": "Remove this row's collections from Plex, for everyone who has it, without waiting for a run.\n\nRemoval only \u2014 it never creates or promotes, so it can never leak: deleting a row can only make\nthe server more private. A per-person row's\ncollection for each user is pinned by the exact title the last run delivered (recorded in that\nrun's breakdown); a shared row is addressed by its own label. dry_run previews the plan.", "operationId": "cleanup_collection_api_collections__collection_id__cleanup_post", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CleanupRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CleanupOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Cleanup Collection", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/effectiveness": { "get": { "description": "How this row has actually performed \u2014 delivered, watched, and the landing rate.\n\nIts own endpoint rather than a slice of `/api/report/effectiveness`, which is ~30 queries and\nruns in a worker thread for that reason; this is four, so opening the row editor costs nothing\nlike opening the dashboard. Both read the same columns through `report_service`, so they cannot\ndrift apart on what counts as a hit.", "operationId": "collection_effectiveness_api_collections__collection_id__effectiveness_get", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RowEffectivenessOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Collection Effectiveness", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/poster/image": { "delete": { "description": "Remove a row's uploaded poster image, and put the artwork on Plex back to default.\n\nClearing the stored bytes used to be all this did, leaving `mode` as \"upload\" with nothing to\nupload \u2014 so the row kept the artwork Shortlist had already pushed to Plex, for ever, with no way\nto reach it: the PATCH path only reverts when a row that HAD a mode drops to none, and the mode\nnever dropped. \"Delete the image\" now means the image is gone from both places.", "operationId": "delete_poster_image_api_collections__collection_id__poster_image_delete", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "responses": { "204": { "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Delete Poster Image", "tags": [ "collections" ] }, "get": { "description": "Serve a row's current poster image: the uploaded original, or a rendered preview.\n\nFor a built-in \"text\" poster the preview is cheap, so it's rendered on demand if not already\ncached (the thumbnail always shows). For an \"ai\" poster only a previously-generated image is\nserved \u2014 a GET never spends money generating one.", "operationId": "get_poster_image_api_collections__collection_id__poster_image_get", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": {} } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Poster Image", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/poster/preview": { "post": { "description": "Render a sample poster from the given text and return the image.\n\nUses sample placeholder values (a name + the Movies library) so the owner can see what a poster\nwill look like. A \"text\" poster always renders (no provider needed); an \"ai\" poster needs an\nimage-capable provider. The result is cached, so warming the preview also speeds the next run.", "operationId": "preview_poster_api_collections__collection_id__poster_preview_post", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PosterIn" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": {} } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Preview Poster", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/poster/upload": { "post": { "description": "Store an uploaded poster image for a row and switch it into upload mode.\n\nNormalizes the image (downscale to poster size + JPEG) before it hits the DB, so a phone photo\ndoesn't bloat /config. Any generate-mode text the user typed is preserved.", "operationId": "upload_poster_image_api_collections__collection_id__poster_upload_post", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_upload_poster_image_api_collections__collection_id__poster_upload_post" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PosterUploadOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Upload Poster Image", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/rename": { "post": { "description": "Rename this row's collections on Plex, streaming SSE events as each user's collection is\nrenamed. Returns a text/event-stream: one 'rename' event per user, then a 'done' event.", "operationId": "rename_collection_stream_api_collections__collection_id__rename_post", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RenameRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": {} } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Rename Collection Stream", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/theme-rotation": { "get": { "description": "Where each person's Explore rotation stands: their current theme, the one queued next, and what came before.", "operationId": "get_theme_rotation_api_collections__collection_id__theme_rotation_get", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ThemeRotationOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Theme Rotation", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/up-next": { "put": { "description": "Point a person's \"Up next\" at a saved theme, replacing any theme already queued. Changes no Plex state.", "operationId": "set_up_next_api_collections__collection_id__up_next_put", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpNextRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ThemeRefOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Set Up Next", "tags": [ "collections" ] } }, "/api/collections/{collection_id}/up-next/regenerate": { "post": { "description": "Write a new \"Up next\" theme for one person now, with one AI call, replacing any theme queued.\n\n409 while the row's AI is paused, 422 without an AI provider or when the row isn't set to Explore.", "operationId": "regenerate_up_next_api_collections__collection_id__up_next_regenerate_post", "parameters": [ { "in": "path", "name": "collection_id", "required": true, "schema": { "title": "Collection Id", "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegenerateRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ThemeRefOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Regenerate Up Next", "tags": [ "collections" ] } }, "/api/events": { "get": { "operationId": "stream_api_events_get", "responses": { "200": { "content": { "text/event-stream": { "schema": { "anyOf": [ { "$ref": "#/components/schemas/RunLogLineOut" }, { "$ref": "#/components/schemas/RunProgressEvent" }, { "$ref": "#/components/schemas/RunFinishedEvent" }, { "$ref": "#/components/schemas/UninstallProgressEvent" }, { "$ref": "#/components/schemas/SyncProgressEvent" }, { "$ref": "#/components/schemas/SyncFinishedEvent" } ], "title": "Response 200 Stream Api Events Get", "type": "string" } } }, "description": "An endless `text/event-stream`. Each frame names its event (`run.user.stage`, `run.progress`, `run.finished`, `uninstall.progress`, `sync.progress`, `sync.finished`) and carries one of these payloads as JSON in `data:`." } }, "summary": "Stream", "tags": [ "events" ] } }, "/api/events/log": { "get": { "description": "The audit trail, newest first. `before_id` pages backwards \u2014 pass the id of the oldest entry\nyou already have. A cursor, not an offset: events are appended while you read. Every filter given\napplies.", "operationId": "audit_log_api_events_log_get", "parameters": [ { "in": "query", "name": "scope", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Scope" } }, { "description": "Only scopes starting with this, e.g. `run.`. Case-insensitive on SQLite (it is a LIKE).", "in": "query", "name": "scope_prefix", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "description": "Only scopes starting with this, e.g. `run.`. Case-insensitive on SQLite (it is a LIKE).", "title": "Scope Prefix" } }, { "description": "Only the scopes that record a write to Plex or plex.tv. Includes dry-run audit rows (`message.dry_run` true), which changed nothing on Plex; the UI labels them as dry runs.", "in": "query", "name": "plex_writes", "required": false, "schema": { "default": false, "description": "Only the scopes that record a write to Plex or plex.tv. Includes dry-run audit rows (`message.dry_run` true), which changed nothing on Plex; the UI labels them as dry runs.", "title": "Plex Writes", "type": "boolean" } }, { "in": "query", "name": "limit", "required": false, "schema": { "default": 200, "maximum": 1000, "minimum": 1, "title": "Limit", "type": "integer" } }, { "in": "query", "name": "before_id", "required": false, "schema": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Before Id" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/EventOut" }, "title": "Response Audit Log Api Events Log Get", "type": "array" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Audit Log", "tags": [ "events" ] } }, "/api/notifications": { "get": { "description": "Every currently-firing notification (update available, failed/partial run, paused, errors),\nplus the ids already dismissed so inline surfaces reporting the same fact can hide themselves.", "operationId": "list_notifications_api_notifications_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NotificationsOut" } } }, "description": "Successful Response" } }, "summary": "List Notifications", "tags": [ "notifications" ] } }, "/api/notifications/dismiss": { "post": { "description": "Hide a notification by id. The id encodes its state (run id / version), so the SAME condition\nstays hidden but a new failure or a newer release surfaces again on its own.", "operationId": "dismiss_api_notifications_dismiss_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Dismiss" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DismissedOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Dismiss", "tags": [ "notifications" ] } }, "/api/notifications/whats-new": { "get": { "description": "The release notes the owner has not read since upgrading, for the What's new dialog.", "operationId": "whats_new_api_notifications_whats_new_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WhatsNewOut" } } }, "description": "Successful Response" } }, "summary": "Whats New", "tags": [ "notifications" ] } }, "/api/notifications/whats-new/seen": { "post": { "description": "Close the What's new dialog for good: record the version whose notes it showed.", "operationId": "whats_new_seen_api_notifications_whats_new_seen_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeenRelease" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DismissedOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Whats New Seen", "tags": [ "notifications" ] } }, "/api/picks/{rating_key}/poster": { "get": { "description": "One delivered pick's artwork, streamed from the PMS.\n\n404 rather than a placeholder image when the item or its artwork is gone: a pick's ratingKey goes\nstale when a title is removed and re-added, and the SPA already renders a tile of the right size\nfor that. Answering with a picture would make a missing item indistinguishable from a real one.\n502 when the PMS itself failed, for the same reason \u2014 an outage is not \"this title has no art\".\n\nArgs:\n rating_key: The pick's Plex ratingKey. `0` means the pipeline never matched the title.\n request: The live request, for the app state and `If-None-Match`.\n\nReturns:\n The image bytes with the PMS's own content type, or a `304` when the browser's copy is\n current.\n\nRaises:\n HTTPException: `404` when there is no artwork to serve, `502` when Plex could not be read.", "operationId": "pick_poster_api_picks__rating_key__poster_get", "parameters": [ { "in": "path", "name": "rating_key", "required": true, "schema": { "title": "Rating Key", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": {} } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Pick Poster", "tags": [ "picks" ] } }, "/api/privacy/status": { "get": { "description": "Every account's live share filter, plus the last enforcement spot-check.\n\nCosts one plex.tv roster read and one PMS collections read. Writes nothing and mints no token, so\nit is safe to call from a screen the owner leaves open.\n\nArgs:\n request: The live request, for app state.\n\nReturns:\n The whole server's sharing state as of now, with the enforcement measurement beside it.", "operationId": "privacy_status_endpoint_api_privacy_status_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PrivacyStatusOut" } } }, "description": "Successful Response" } }, "summary": "Privacy Status Endpoint", "tags": [ "privacy" ] } }, "/api/report": { "get": { "description": "The dashboard tracking report: headline counts for the chosen window with their change vs the\nprevious equal period, the landing rate over a matured cohort, watch momentum, per-user and\nper-row breakdowns, the titles landing best, requests that paid off, and a recent-watches feed.\n\nDeliberately a plain `def`, not `async def`: the computation is ~30 synchronous queries, one of\nthem a `GROUP BY user_id, tmdb_id, media_type` over the whole picks table. On the event loop that\nstalled SSE, `/api/system/health` and every other request for as long as it took; as a sync\nhandler Starlette runs it in a worker thread instead.", "operationId": "effectiveness_api_report_get", "parameters": [ { "description": "Report window in days: 7, 30, 90, or 'all'.", "in": "query", "name": "window", "required": false, "schema": { "default": "30", "description": "Report window in days: 7, 30, 90, or 'all'.", "title": "Window", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EffectivenessReportOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Effectiveness", "tags": [ "report" ] } }, "/api/report/deleted-rows": { "delete": { "description": "Permanently delete the history of rows that no longer exist. `slug` clears just one.\n\n\"History\", not \"pick history\": a SHARED row writes no `picks` at all, so what is removed for one\nis its `shared_row_watches` credits. The returned `picks` field counts BOTH (its name is kept for\nwire compatibility); the audit event splits them as `pick_rows` + `shared_watches`.\n\nThis is the one destructive action on the dashboard, so it is deliberately narrow:\n\n* Only slugs with no live Collection are eligible \u2014 the eligible set is recomputed here rather\n than trusted from the request, so naming a live row's slug deletes nothing.\n* Only `picks` and `shared_row_watches` rows are touched. `deliveries` is left alone: it is the\n ledger of what still exists on Plex, and clearing it would strand a real collection with nothing\n left to clean it up.\n* These picks disappear from everywhere they are counted \u2014 not just this dashboard. The per-user\n lifetime stats and a person's pick history on their own page are read from the same rows, so the\n UI has to say \"history\", not \"the numbers above\".", "operationId": "clear_deleted_rows_api_report_deleted_rows_delete", "parameters": [ { "in": "query", "name": "slug", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Slug" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClearedDeletedRowsOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Clear Deleted Rows", "tags": [ "report" ] }, "get": { "description": "History belonging to rows that no longer exist, with how much of it there is.\n\n\"History\", not \"pick history\": a SHARED row writes no `picks` at all, so for one of those the\n`picks` field counts its `shared_row_watches` credits. The field name is kept for wire\ncompatibility; see `DeletedRowOut.picks`.\n\nIts own endpoint rather than a field on the report: the report is windowed, and \"what can I clear\"\nis a question about ALL of a deleted row's history, not the last 30 days of it.", "operationId": "deleted_rows_api_report_deleted_rows_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/DeletedRowOut" }, "title": "Response Deleted Rows Api Report Deleted Rows Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "Deleted Rows", "tags": [ "report" ] } }, "/api/report/engagement": { "get": { "description": "What people did with their picks: per person, per title, and where abandons cluster.\n\nThe detail behind the dashboard's Dropped tile. Sync like `effectiveness` and for the same reason\n\u2014 it is a scan of `picks`, and running that on the event loop stalls SSE and every other request.", "operationId": "engagement_api_report_engagement_get", "parameters": [ { "description": "Report window in days: 7, 30, 90, or 'all'.", "in": "query", "name": "window", "required": false, "schema": { "default": "30", "description": "Report window in days: 7, 30, 90, or 'all'.", "title": "Window", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EngagementOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Engagement", "tags": [ "report" ] } }, "/api/report/sync": { "post": { "description": "Run the daily watch-status sync on demand \u2014 refresh every user's watched picks now.\n\nQueued as a real `sync.history` JOB, the same one the Jobs page runs, rather than the bare\nbackground task this used to be. That task did the work but left no `jobs` row, so nothing that\ntracks jobs could see it: no toast, nothing in the header's activity popover, nothing in Jobs \u2192\nActivity. The Jobs page appeared to show it only because its progress bar listens to the SSE\n`sync.progress` stream \u2014 a second, unrelated channel. One button, two mechanisms, and only one of\nthem visible.\n\nGoing through the queue also means it is durable and retried, which fire-and-forget never was.", "operationId": "trigger_sync_api_report_sync_post", "responses": { "202": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SyncStartedOut" } } }, "description": "Successful Response" } }, "summary": "Trigger Sync", "tags": [ "report" ] } }, "/api/requests": { "get": { "description": "The whole inbox: pending first (most-wanted, best-rated on top), then sent, then rejected.\n\nRows the owner cleared from the Sent log (``hidden``) are excluded \u2014 they stay in the DB as sent\ntombstones (so the title isn't re-requested) but never show in the UI again.\n\nArgs:\n request: The FastAPI request, for the session factory.\n wanted_by: Repeated query parameter (``?wanted_by=sarah&wanted_by=mike``) naming the people\n whose titles to keep \u2014 matched against ``wanters``, which holds bare Plex usernames.\n A title is kept if ANY of the named people wanted it (union, not intersection), matching\n what the inbox's \"Wanted by\" chips mean. Omitted (or empty) means everyone, which is the\n unfiltered inbox \u2014 no caller that leaves it off sees any change.\n\nReturns:\n The matching rows, capped at :data:`MAX_INBOX`.\n\nThe cap is applied AFTER the status sort, in Python, because the ordering is by\n(status, demand, rating) and a SQL LIMIT before that sort would cut arbitrary rows rather than\nthe tail of the history. ``wanted_by`` is applied BEFORE the cap \u2014 a filter applied to the capped\npage could only ever search the 500 rows the cap left, and \"what does this new person still\nneed?\" is precisely the question that wants everything on file for one person.\n\nThe name filter runs in Python rather than SQL: the read below is already unbounded (`.all()`\nover every non-hidden row \u2014 the cap bounds the PAYLOAD, not the query), so filtering the rows\nalready in memory costs nothing extra, and it avoids depending on SQLite's JSON1 `json_each` to\nask whether a JSON array column contains a value.", "operationId": "list_requests_api_requests_get", "parameters": [ { "description": "Only titles at least one of these people wanted (the `wanters` usernames).", "in": "query", "name": "wanted_by", "required": false, "schema": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "description": "Only titles at least one of these people wanted (the `wanters` usernames).", "title": "Wanted By" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/RequestCandidateOut" }, "title": "Response List Requests Api Requests Get", "type": "array" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "List Requests", "tags": [ "requests" ] } }, "/api/requests/acquisition-claims": { "get": { "description": "Show durable acquisition reservations and uncertain outcomes to their owner.", "operationId": "acquisition_claims_api_requests_acquisition_claims_get", "parameters": [ { "in": "query", "name": "limit", "required": false, "schema": { "default": 100, "maximum": 100, "minimum": 1, "title": "Limit", "type": "integer" } }, { "in": "query", "name": "offset", "required": false, "schema": { "default": 0, "maximum": 100000, "minimum": 0, "title": "Offset", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Acquisition Claims Api Requests Acquisition Claims Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Acquisition Claims", "tags": [ "requests" ] } }, "/api/requests/acquisition-claims/{claim_id}/release": { "post": { "description": "Release an exact terminal claim after the owner has checked the remote destination.", "operationId": "release_acquisition_claim_api_requests_acquisition_claims__claim_id__release_post", "parameters": [ { "in": "path", "name": "claim_id", "required": true, "schema": { "title": "Claim Id", "type": "integer" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AcquisitionReleaseIn" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Release Acquisition Claim Api Requests Acquisition Claims Claim Id Release Post", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Release Acquisition Claim", "tags": [ "requests" ] } }, "/api/requests/clear": { "post": { "description": "Clear the given SENT titles from the send log \u2014 hide them, don't delete them.\n\nA sent row is a load-bearing tombstone: dropping it lets a still-downloading title look \"missing\"\nand get re-requested every night (see ``delete_requests``). So \"clear\" sets ``hidden`` instead \u2014\nthe row stays ``sent`` and keeps protecting against re-request, but never shows in the inbox again.\nOnly ``sent`` rows are cleared; a pending/rejected id is ignored (those have Delete / Reject).", "operationId": "clear_requests_api_requests_clear_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestAction" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClearedOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Clear Requests", "tags": [ "requests" ] } }, "/api/requests/delete": { "post": { "description": "Remove the given titles from the inbox entirely, leaving no trace.\n\nUnlike ``/reject`` (a permanent tombstone), a deleted row is gone \u2014 so if a later run's picks turn\nup the same title again, it returns to the pending queue. Two uses: clear a title off the list\nwithout blocking it forever, or delete a previously *rejected* title to let it come back.\n\n``sent`` rows are never deleted: that status is a load-bearing tombstone (``_persist_request_queue``)\nthat stops a still-downloading title from being seen as \"missing\" and re-requested every night.\nDropping it would resurrect that bug, so a ``sent`` id in the request is skipped, not deleted.", "operationId": "delete_requests_api_requests_delete_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestAction" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeletedOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Delete Requests", "tags": [ "requests" ] } }, "/api/requests/hold-preview": { "post": { "description": "Which movies waiting in the inbox these genres/tags would hold \u2014 the settings page's live check.\n\nReads TMDB through the shared cache, so a title a run already looked at costs nothing. Nothing is saved.", "operationId": "hold_preview_api_requests_hold_preview_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HoldPreviewIn" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HoldPreviewOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Hold Preview", "tags": [ "requests" ] } }, "/api/requests/reject": { "post": { "description": "Permanently dismiss the given titles.\n\nA rejected title is kept on file as a tombstone: it leaves the pending list AND every later run\nskips re-queuing it (``_persist_request_queue`` only touches ``pending`` rows), so a dismissed\nsuggestion can never come back on its own. Use ``/delete`` instead to remove a title without\nblocking it \u2014 or to lift a rejection so a future run may surface it again.", "operationId": "reject_requests_api_requests_reject_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestAction" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RejectedOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Reject Requests", "tags": [ "requests" ] } }, "/api/requests/restore": { "post": { "description": "Un-reject: move rejected titles back to the pending queue (Waiting) so they can be sent again.\n\nOnly ``rejected`` rows are restored; ``pending``/``sent`` are left as they are. The row keeps its\nrecorded demand/wanters/why/tags, so it reappears in Waiting exactly as it was, ready to send \u2014\nunlike a run, which would only re-surface it if the same taste turned it up again.", "operationId": "restore_requests_api_requests_restore_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestAction" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RestoredOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Restore Requests", "tags": [ "requests" ] } }, "/api/requests/row-sources": { "get": { "description": "Read every request source once and say whether a \"Your requests\" row can be built from it.\n\nRead-only: nothing is written to Overseerr, the Arrs, or Plex. A source that is down reads as\n\"unreachable\" with the reason in `problems` \u2014 never a 500, because the screen this feeds exists\nprecisely to show the owner what is wrong.", "operationId": "get_row_sources_api_requests_row_sources_get", "parameters": [ { "description": "An own-tag pattern to preview, e.g. req-{username}", "in": "query", "name": "pattern", "required": false, "schema": { "default": "", "description": "An own-tag pattern to preview, e.g. req-{username}", "maxLength": 128, "title": "Pattern", "type": "string" } }, { "description": "The requests row being edited. Its pattern is the typed one; every OTHER enabled requests row's pattern joins it, because a run judges a tag against all of them. Omitted, every enabled requests row's saved pattern joins the typed one.", "in": "query", "name": "row_id", "required": false, "schema": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "description": "The requests row being edited. Its pattern is the typed one; every OTHER enabled requests row's pattern joins it, because a run judges a tag against all of them. Omitted, every enabled requests row's saved pattern joins the typed one.", "title": "Row Id" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RowSourcesOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Row Sources", "tags": [ "requests" ] } }, "/api/requests/send": { "post": { "description": "Ask Sonarr/Radarr for the chosen pending titles.\n\nA dry run previews the outcomes without asking and leaves every row pending. A real send marks a\nrow ``sent`` only when the app accepted it. Known skips leave it available for another attempt;\nuncertain outcomes keep a durable claim until the owner checks the destination and releases it.", "operationId": "send_requests_api_requests_send_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestAction" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SendOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Send Requests", "tags": [ "requests" ] } }, "/api/requests/status": { "get": { "description": "Arr download status for every request row, keyed by request id, plus per-app reachability.\n\nCovers waiting rows as well as sent ones. A waiting title is normally absent from the Arrs \u2014 the\nnightly pass drops anything they already track \u2014 so a status there means the owner (or another\ntool) added it by hand since, which is exactly the case where \"why is this still waiting?\" needs\nan answer. Rejected rows are skipped: nothing is going to happen to them.\n\nWhole-library maps, not per-title lookups, so the cost is a handful of calls no matter how long\nthe inbox is \u2014 which is what makes it cheap enough for the inbox to poll. Runs in an executor\nsince the Arr clients are sync. A title neither app tracks appears as None.", "operationId": "get_arr_status_api_requests_status_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ArrStatusOut" } } }, "description": "Successful Response" } }, "summary": "Get Arr Status", "tags": [ "requests" ] } }, "/api/runs": { "delete": { "description": "Delete all run history (the Runs list and per-user detail/traces). Picks are KEPT so the\ndashboard's lifetime metrics survive. Delivered membership is independent, so watch tracking\nand carry-forward keep working. Changes nothing on Plex.", "operationId": "clear_runs_api_runs_delete", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunsDeletedOut" } } }, "description": "Successful Response" } }, "summary": "Clear Runs", "tags": [ "runs" ] }, "get": { "description": "Recent runs, newest first. `collection` (a row slug) narrows to runs that actually built that\nrow \u2014 the ones whose picks carry its slug \u2014 so the Rows page can link a row to its own history.\n\n`before_id` pages backwards through history: pass the id of the oldest run you already have.\nCursor rather than offset because runs are inserted while you read \u2014 an offset would skip or\nrepeat rows as the list shifts under you.", "operationId": "list_runs_api_runs_get", "parameters": [ { "in": "query", "name": "limit", "required": false, "schema": { "default": 50, "maximum": 200, "minimum": 1, "title": "Limit", "type": "integer" } }, { "in": "query", "name": "collection", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Collection" } }, { "in": "query", "name": "before_id", "required": false, "schema": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Before Id" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/RunSummaryOut" }, "title": "Response List Runs Api Runs Get", "type": "array" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "List Runs", "tags": [ "runs" ] }, "post": { "operationId": "trigger_run_api_runs_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunRequest" } } }, "required": true }, "responses": { "202": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunCreatedOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Trigger Run", "tags": [ "runs" ] } }, "/api/runs/summary": { "get": { "description": "Totals for the Runs page header: how many runs, how many succeeded/failed, and the last one.", "operationId": "runs_summary_api_runs_summary_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunsSummaryOut" } } }, "description": "Successful Response" } }, "summary": "Runs Summary", "tags": [ "runs" ] } }, "/api/runs/{run_id}": { "get": { "operationId": "get_run_api_runs__run_id__get", "parameters": [ { "in": "path", "name": "run_id", "required": true, "schema": { "title": "Run Id", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunDetailOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Run", "tags": [ "runs" ] } }, "/api/runs/{run_id}/cancel": { "post": { "description": "Ask the in-flight run to stop. Cooperative \u2014 it finishes the person it's on, then stops, and\nstill merges the privacy filters + promotes everyone delivered so far.\n\nIdempotent: asking twice succeeds, because a run that is already stopping is the state the caller\nwanted. Only a run this process isn't executing at all gets the 409 \u2014 which is what that message\nhas always claimed and, until the second press stopped being an error, frequently was not.", "operationId": "cancel_run_api_runs__run_id__cancel_post", "parameters": [ { "in": "path", "name": "run_id", "required": true, "schema": { "title": "Run Id", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunCancelledOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Cancel Run", "tags": [ "runs" ] } }, "/api/runs/{run_id}/log": { "get": { "description": "The run's activity log: per-user stages, then the server-wide phases that follow them.\n\nServed from the live in-memory tail while the run is in flight and from `run_log_lines`\nafterwards, so a run whose process has since restarted still has a readable log.\n\n`after_seq` returns only lines past that sequence number, for topping up without refetching the\nwhole feed. `format=text` renders it as plain text for the download button \u2014 that branch returns\na `Response` directly, which FastAPI hands back untouched, so the schema describes the JSON one.", "operationId": "get_run_log_api_runs__run_id__log_get", "parameters": [ { "in": "path", "name": "run_id", "required": true, "schema": { "title": "Run Id", "type": "integer" } }, { "in": "query", "name": "after_seq", "required": false, "schema": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "After Seq" } }, { "in": "query", "name": "format", "required": false, "schema": { "default": "json", "title": "Format", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/RunLogLineOut" }, "title": "Response Get Run Log Api Runs Run Id Log Get", "type": "array" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Run Log", "tags": [ "runs" ] } }, "/api/runs/{run_id}/rows/{collection_slug}/trace": { "get": { "description": "The full pipeline trace for one SHARED row in one run \u2014 the per-row twin of the user trace.\n\nSame response model, because a shared row runs the same pipeline minus the per-person history\nstage (it is built from pooled watching, so it records `gathers` and no `history`). The row's\nTITLE is served as `username`/`display_name` so a single trace view renders both without forking.\n\nEmpty `{}` on a run recorded before shared rows were persisted \u2014 the UI reads that as \"no trace\nfor this run\", not an error, exactly as it does for a user.", "operationId": "get_run_shared_row_trace_api_runs__run_id__rows__collection_slug__trace_get", "parameters": [ { "in": "path", "name": "run_id", "required": true, "schema": { "title": "Run Id", "type": "integer" } }, { "in": "path", "name": "collection_slug", "required": true, "schema": { "title": "Collection Slug", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunUserTraceOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Run Shared Row Trace", "tags": [ "runs" ] } }, "/api/runs/{run_id}/users/{user_id}/trace": { "get": { "description": "The full pipeline trace for one user in one run: seeds derived, each candidate source's\nqueries and returns, the web-search / RAG prompts, and the titles proposed. Fetched on demand\n(it's a large blob) so the run-detail page stays light. Empty ``{}`` on a run predating the\nfeature or a skipped/cold user \u2014 the UI reads that as \"no trace for this run\", not an error.", "operationId": "get_run_user_trace_api_runs__run_id__users__user_id__trace_get", "parameters": [ { "in": "path", "name": "run_id", "required": true, "schema": { "title": "Run Id", "type": "integer" } }, { "in": "path", "name": "user_id", "required": true, "schema": { "title": "User Id", "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunUserTraceOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Get Run User Trace", "tags": [ "runs" ] } }, "/api/schedule": { "get": { "description": "Everything on a timer, each with its cron, its next fire time, and how to change it.\n\nRows are grouped by shared cron, exactly as the scheduler groups them \u2014 three rows on `30 3 * * *`\nare ONE trigger that builds all three, and listing them as three separate 03:30 entries would\nmisrepresent what the server does.", "operationId": "schedule_api_schedule_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleOut" } } }, "description": "Successful Response" } }, "summary": "Schedule", "tags": [ "schedule" ] } }, "/api/seasons": { "get": { "description": "Every season a row can follow, in calendar order: the built-ins and the owner's own.", "operationId": "list_seasons_api_seasons_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/SeasonOut" }, "title": "Response List Seasons Api Seasons Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "List Seasons", "tags": [ "seasons" ] }, "post": { "description": "Save a new season. Its slug is made from its name now and never changes (D14).", "operationId": "create_season_api_seasons_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeasonIn" } } }, "required": true }, "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeasonOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Create Season", "tags": [ "seasons" ] } }, "/api/seasons/library-search": { "get": { "description": "Films and shows in the libraries whose title contains ``q``, for picking by hand.", "operationId": "library_search_api_seasons_library_search_get", "parameters": [ { "in": "query", "name": "q", "required": false, "schema": { "default": "", "title": "Q", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/LibraryTitleOut" }, "title": "Response Library Search Api Seasons Library Search Get", "type": "array" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Library Search", "tags": [ "seasons" ] } }, "/api/seasons/next-date": { "post": { "description": "When a draft date rule next falls, or why it can't be used \u2014 from the rule alone.\n\nThe editor's \"Next: \u2026\" line asks this rather than the count, so a count that fails (no TMDB key, Plex\ndown) never takes the date with it. Reads no clock but the server's, and nothing else.", "operationId": "next_date_api_seasons_next_date_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DateRuleIO" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeasonDateOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Next Date", "tags": [ "seasons" ] } }, "/api/seasons/plex-collections": { "get": { "description": "The libraries' collections whose title contains ``q``, in any case, by title. Never one of Shortlist's.", "operationId": "plex_collections_api_seasons_plex_collections_get", "parameters": [ { "in": "query", "name": "q", "required": false, "schema": { "default": "", "title": "Q", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/PlexCollectionOut" }, "title": "Response Plex Collections Api Seasons Plex Collections Get", "type": "array" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Plex Collections", "tags": [ "seasons" ] } }, "/api/seasons/presets": { "get": { "description": "The ready-made seasons not added yet (#137 D9).", "operationId": "list_presets_api_seasons_presets_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/PresetOut" }, "title": "Response List Presets Api Seasons Presets Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "List Presets", "tags": [ "seasons" ] } }, "/api/seasons/preview": { "post": { "description": "Count a draft season's titles in one row's libraries, as a run would, without saving anything.\n\nOnly the row's own media type and libraries count: that is all the row draws from (#137 I-1). An invalid\ndate rule still counts: the editor shows what is wrong with the date beside the count.", "operationId": "preview_season_api_seasons_preview_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeasonPreviewIn" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeasonPreviewOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Preview Season", "tags": [ "seasons" ] } }, "/api/seasons/tmdb-tags": { "get": { "description": "TMDB tags whose name matches ``q``, each with how many films TMDB tags with it.", "operationId": "tmdb_tags_api_seasons_tmdb_tags_get", "parameters": [ { "in": "query", "name": "q", "required": false, "schema": { "default": "", "title": "Q", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/TagOut" }, "title": "Response Tmdb Tags Api Seasons Tmdb Tags Get", "type": "array" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Tmdb Tags", "tags": [ "seasons" ] } }, "/api/seasons/{slug}": { "delete": { "description": "Untick a custom season and save its required visibility work in one transaction.", "operationId": "delete_season_api_seasons__slug__delete", "parameters": [ { "in": "path", "name": "slug", "required": true, "schema": { "title": "Slug", "type": "string" } } ], "responses": { "204": { "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Delete Season", "tags": [ "seasons" ] }, "put": { "description": "Replace a custom season and atomically record owed visibility work.", "operationId": "update_season_api_seasons__slug__put", "parameters": [ { "in": "path", "name": "slug", "required": true, "schema": { "title": "Slug", "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeasonIn" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeasonOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Update Season", "tags": [ "seasons" ] } }, "/api/settings": { "get": { "operationId": "get_settings_api_settings_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettingsOut" } } }, "description": "Successful Response" } }, "summary": "Get Settings", "tags": [ "settings" ] }, "put": { "operationId": "put_settings_api_settings_put", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettingsUpdate" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettingsOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Put Settings", "tags": [ "settings" ] } }, "/api/settings/arr/{service}/options": { "get": { "description": "Quality profiles + root folders for a connected Sonarr/Radarr, so the UI offers dropdowns\nrather than asking a non-technical owner to hunt down numeric profile ids and server paths.", "operationId": "arr_options_api_settings_arr__service__options_get", "parameters": [ { "in": "path", "name": "service", "required": true, "schema": { "title": "Service", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ArrOptionsOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Arr Options", "tags": [ "settings" ] } }, "/api/settings/curator/models": { "post": { "description": "Model ids an AI provider offers, for the model picker.\n\nLists the provider being edited: the request may carry the (unsaved) provider + key/URL from the\nsettings form, so switching provider or typing a new key updates the dropdown live. Blank fields\nfall back to the SAVED settings, and a redacted key means 'use the saved key'. The key builds the\nclient in memory only \u2014 never logged (only the exception CLASS is, since an SDK can embed the key\nin error text). Best-effort: no key yet, an offline Ollama, or a provider without a models\nendpoint returns an empty list, and the UI falls back to the free-text override.", "operationId": "curator_models_api_settings_curator_models_post", "requestBody": { "content": { "application/json": { "schema": { "anyOf": [ { "$ref": "#/components/schemas/CuratorModelsRequest" }, { "type": "null" } ], "title": "Body" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CuratorModelsOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Curator Models", "tags": [ "settings" ] } }, "/api/settings/defaults": { "get": { "description": "Every setting's built-in default, so the page can mark one the owner has changed.\n\nSecrets and private keys are left out: a default carries no credential, and what is stored under\nthose keys is never this endpoint's to say.", "operationId": "get_setting_defaults_api_settings_defaults_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SettingsOut" } } }, "description": "Successful Response" } }, "summary": "Get Setting Defaults", "tags": [ "settings" ] } }, "/api/settings/overseerr/options": { "get": { "description": "The instance's accounts, so the UI can offer a \"request as\" dropdown.\n\nThe *seerr equivalent of ``arr_options``, and deliberately much smaller: quality profiles and\nroot folders are Overseerr's business on this route, so the only choice left to Shortlist is\nwhose name the request goes out under.", "operationId": "overseerr_options_api_settings_overseerr_options_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SeerrOptionsOut" } } }, "description": "Successful Response" } }, "summary": "Overseerr Options", "tags": [ "settings" ] } }, "/api/settings/test/{service}": { "post": { "description": "One tiny call per service; returns plain-English ok/error (design: everything re-testable).", "operationId": "test_connection_api_settings_test__service__post", "parameters": [ { "in": "path", "name": "service", "required": true, "schema": { "title": "Service", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConnectionTestOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Test Connection", "tags": [ "settings" ] } }, "/api/setup/link": { "post": { "description": "Persist the chosen server; the authenticated account must be its owner.", "operationId": "link_server_api_setup_link_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkedOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Link Server", "tags": [ "setup" ] } }, "/api/setup/probe": { "post": { "description": "Capability checklist for wizard step 1: version gate, Plex Pass, libraries, Tautulli.", "operationId": "probe_api_setup_probe_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProbeRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProbeResultOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Probe", "tags": [ "setup" ] } }, "/api/setup/servers": { "get": { "description": "Every Plex server this account can reach, with each advertised address tested.\n\nThis is what makes the URL field a picker instead of a guess: Plex advertises several\naddresses per server (LAN, hostname, relay), and only the owner's network knows which one\nactually works from where Shortlist runs \u2014 so we try them all and report what answered.", "operationId": "list_servers_api_setup_servers_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/PlexServerOut" }, "title": "Response List Servers Api Setup Servers Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "List Servers", "tags": [ "setup" ] } }, "/api/setup/state": { "get": { "operationId": "get_state_api_setup_state_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WizardStateOut" } } }, "description": "Successful Response" } }, "summary": "Get State", "tags": [ "setup" ] }, "put": { "operationId": "put_state_api_setup_state_put", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WizardState" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WizardStateSavedOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Put State", "tags": [ "setup" ] } }, "/api/support/ai": { "get": { "description": "What the AI curator actually did for this person: tokens spent per step, and any error.", "operationId": "ai_decisions_api_support_ai_get", "parameters": [ { "in": "query", "name": "user", "required": true, "schema": { "minLength": 1, "title": "User", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Ai Decisions Api Support Ai Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Ai Decisions", "tags": [ "support" ] } }, "/api/support/bundle.txt": { "get": { "description": "Every tool's block in one file, for when a paste would be truncated.\n\nDiscord collapses long messages and Reddit truncates them, so past a certain size the right\nanswer is an attachment rather than a paste. Deliberately over-collects: a second round trip\nwith a non-technical reporter costs a day, and redundant text costs nothing.", "operationId": "bundle_api_support_bundle_txt_get", "responses": { "200": { "content": { "text/plain": { "schema": { "type": "string" } } }, "description": "Successful Response" } }, "summary": "Bundle", "tags": [ "support" ] } }, "/api/support/clocks": { "get": { "description": "Timezones and the next scheduled fire times.\n\nEvery timestamp in the database is UTC and every line in the log is local. Reading one as the\nother inverts the order of events, which has cost this project real debugging time \u2014 so the\noffset is stated outright rather than left to be inferred.", "operationId": "clocks_api_support_clocks_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Clocks Api Support Clocks Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Clocks", "tags": [ "support" ] } }, "/api/support/config": { "get": { "description": "Where each setting's value came from: an environment variable, or the database.\n\nEnv vars are ONE-TIME seeds \u2014 read into the database on first boot and ignored for ever after \u2014\nwhich surprises people who then edit their compose file and see nothing change. This says so per\nkey. Secret values are never rendered; only whether one is set.", "operationId": "config_api_support_config_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Config Api Support Config Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Config", "tags": [ "support" ] } }, "/api/support/connection": { "get": { "description": "Per person: do we hold a working share token, and which libraries has it ever read?\n\nA library that silently refuses someone's token is indistinguishable from a person who watches\nnothing \u2014 both produce an empty watched set. Only this pairing separates them, and getting it\nwrong sends a maintainer hunting the recommendation engine for a permissions bug.", "operationId": "connection_api_support_connection_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Connection Api Support Connection Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Connection", "tags": [ "support" ] } }, "/api/support/database": { "get": { "description": "Migration head plus proof the schema actually has what the ORM claims.\n\nHead alone is not health: a migration that no-ops on a real database still stamps its version,\nand this project has shipped exactly that. So the tables and indexes are counted, not assumed.", "operationId": "database_api_support_database_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Database Api Support Database Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Database", "tags": [ "support" ] } }, "/api/support/disable": { "post": { "description": "Switch support mode off immediately, without waiting for the expiry.", "operationId": "disable_api_support_disable_post", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusOut" } } }, "description": "Successful Response" } }, "summary": "Disable", "tags": [ "support" ] } }, "/api/support/drift": { "get": { "description": "Does Plex match the ledger? The register of a bug class with fifteen recorded instances.\n\nThe database records that a row was delivered; the server is where it either exists or does not.\nNothing today compares the two, so a write that silently failed looks identical to one that\nworked. Read-only by design: this reports drift and never repairs it, because repair belongs to a\nrun, where snapshots and dry-run already live.", "operationId": "drift_api_support_drift_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Drift Api Support Drift Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Drift", "tags": [ "support" ] } }, "/api/support/enable": { "post": { "description": "Switch support mode on for `_SUPPORT_HOURS`. Re-enabling extends from now, it does not stack.", "operationId": "enable_api_support_enable_post", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusOut" } } }, "description": "Successful Response" } }, "summary": "Enable", "tags": [ "support" ] } }, "/api/support/errors": { "get": { "description": "Recent WARNING and ERROR log lines \u2014 the part of a bug report nobody remembers to attach.\n\nEverything here is already redacted by `log_reader.scrub` (an alias for `http_retry.redact`),\nwhich covers more credential shapes than this module's own `_scrub`: query params, header forms,\nBearer credentials. Over-redaction is fine; a leaked token is not (rule 9).\n\nCapped hard, because this rides along in a report someone pastes into a chat window. When the tail\nis not enough, the full redacted log zip is a separate download (`/api/system/logs/download`) and\nthe block says so.", "operationId": "errors_api_support_errors_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Errors Api Support Errors Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Errors", "tags": [ "support" ] } }, "/api/support/funnel": { "get": { "description": "Counts at each stage from TMDB pool to delivered row \u2014 \"why is my row short\", answered.\n\nA short row has one cause per stage, and they need completely different fixes: a thin library, an\nover-tight watched cap, a genre filter, or the AI declining. Only the counts distinguish them.", "operationId": "funnel_api_support_funnel_get", "parameters": [ { "in": "query", "name": "user", "required": true, "schema": { "minLength": 1, "title": "User", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Funnel Api Support Funnel Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Funnel", "tags": [ "support" ] } }, "/api/support/health": { "get": { "description": "The overview strip: is each moving part working, answered independently.", "operationId": "health_api_support_health_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Health Api Support Health Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Health", "tags": [ "support" ] } }, "/api/support/jobs": { "get": { "description": "The durable queue: what is stuck, what failed, and with what error.", "operationId": "jobs_api_support_jobs_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Jobs Api Support Jobs Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Jobs", "tags": [ "support" ] } }, "/api/support/libraries": { "get": { "description": "Every library with its key, type and TMDB-id coverage.\n\nA library whose TYPE is not what everyone assumes, or that holds titles Plex never matched, is\ninvisible in a log and explains a whole class of \"nothing gets recommended from here\".", "operationId": "libraries_api_support_libraries_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Libraries Api Support Libraries Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Libraries", "tags": [ "support" ] } }, "/api/support/missing": { "get": { "description": "Why a title never appeared \u2014 usually the real question, and nothing else answers it.\n\nWalks the last run's trace for this person and reports the first stage that rejected the title:\nit was never pooled, the library does not hold it, it counted as already-watched, a genre filter\ndropped it, or it simply lost the ranking cut.", "operationId": "why_missing_api_support_missing_get", "parameters": [ { "in": "query", "name": "user", "required": true, "schema": { "minLength": 1, "title": "User", "type": "string" } }, { "in": "query", "name": "title", "required": true, "schema": { "minLength": 1, "title": "Title", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Why Missing Api Support Missing Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Why Missing", "tags": [ "support" ] } }, "/api/support/person/{slug}": { "get": { "description": "One person's watch read, library by library \u2014 the tool that separates \"watches nothing\" from\n\"a library silently refused their token\", which look identical everywhere else.", "operationId": "person_api_support_person__slug__get", "parameters": [ { "in": "path", "name": "slug", "required": true, "schema": { "title": "Slug", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Person Api Support Person Slug Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Person", "tags": [ "support" ] } }, "/api/support/pick": { "get": { "description": "Why a delivered title is in someone's row: the seed it came from, the source, its strength.", "operationId": "why_here_api_support_pick_get", "parameters": [ { "in": "query", "name": "user", "required": true, "schema": { "minLength": 1, "title": "User", "type": "string" } }, { "in": "query", "name": "title", "required": true, "schema": { "minLength": 1, "title": "Title", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Why Here Api Support Pick Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Why Here", "tags": [ "support" ] } }, "/api/support/read-as": { "get": { "description": "Read the PMS AS one person, using their own share token, and show the raw response.\n\nThe one thing nobody can do from a browser: a maintainer cannot log in as someone else's Plex\naccount, and the owner's own view answers a different question. Reading the library through the\nshare token is what settles \"is this actually marked watched for THEM\".", "operationId": "read_as_api_support_read_as_get", "parameters": [ { "in": "query", "name": "user", "required": true, "schema": { "minLength": 1, "title": "User", "type": "string" } }, { "in": "query", "name": "endpoint", "required": false, "schema": { "default": "libraries", "title": "Endpoint", "type": "string" } }, { "in": "query", "name": "section", "required": false, "schema": { "default": "", "title": "Section", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Read As Api Support Read As Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Read As", "tags": [ "support" ] } }, "/api/support/report.zip": { "get": { "description": "The whole report AND every redacted log file, as one attachment.\n\nThe text report is deliberately capped so it can be pasted into a chat window \u2014 newest 40\nwarnings, five runs. That is enough to name most problems and not enough for one that needs\nhistory, and asking someone to then find the Logs page and export separately is a step that does\nnot happen. This is the one button for \"give them everything\".\n\nLog files are redacted by `log_reader.build_zip`, file by file, because an export is the single\nmost likely thing to end up in a public issue tracker.", "operationId": "report_zip_api_support_report_zip_get", "responses": { "200": { "content": { "application/json": { "schema": {} } }, "description": "Successful Response" } }, "summary": "Report Zip", "tags": [ "support" ] } }, "/api/support/row-schedule": { "get": { "description": "When each row last rebuilt, and when it is next due to.\n\nThe gap this closes: the refresh cadence is exactly that \u2014 a cadence. At the 8-day default a row\nre-selects its titles about weekly and redelivers the same picks on every other night, and the\nengine logs that decision NOWHERE. So \"I changed the setting and nothing happened\" has been\nunanswerable \u2014 the setting was fine, the row simply had not rebuilt yet.", "operationId": "row_schedule_api_support_row_schedule_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Row Schedule Api Support Row Schedule Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Row Schedule", "tags": [ "support" ] } }, "/api/support/rows": { "get": { "description": "Every row's effective settings, with the SOURCE of each winning value.\n\n\"But I set it to 0%\" is answerable only by showing whether the global default or a per-row\noverride actually applied \u2014 the two are indistinguishable from the row editor alone.", "operationId": "rows_api_support_rows_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Rows Api Support Rows Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Rows", "tags": [ "support" ] } }, "/api/support/runs": { "get": { "description": "The last few runs, and WHO failed in each \u2014 the other thing a report always needs.\n\nThe timeline says a run finished with a status. This says which people it could not build and why,\nwhich is the difference between \"a run failed\" and something actionable.", "operationId": "recent_runs_api_support_runs_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Recent Runs Api Support Runs Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Recent Runs", "tags": [ "support" ] } }, "/api/support/settings-history": { "get": { "description": "Settings changes, newest first \u2014 turning \"but I set it to 0%\" into a timestamp.\n\nPaired with the row schedule: a change made after a row's last rebuild has not reached that row\nyet, which is the commonest reason a correct setting appears to do nothing.", "operationId": "settings_history_api_support_settings_history_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Settings History Api Support Settings History Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Settings History", "tags": [ "support" ] } }, "/api/support/sharing": { "get": { "description": "Every account's live share filters, with OUR exclusions separated from everything else.\n\nRows are made private by merging `label!=shortlist_` into each other account's filters, and\nthe event log records what CHANGED. Nothing shows what is true right now \u2014 so a filter that was\nnever written, or was overwritten by another tool, is invisible until someone reports a leak.", "operationId": "sharing_api_support_sharing_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Sharing Api Support Sharing Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Sharing", "tags": [ "support" ] } }, "/api/support/status": { "get": { "description": "Whether the diagnostic tools are currently usable, and for how much longer.", "operationId": "status_api_support_status_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusOut" } } }, "description": "Successful Response" } }, "summary": "Status", "tags": [ "support" ] } }, "/api/support/suggestions": { "get": { "description": "People and titles to offer as you type, for the checks that take a name.\n\nExists because a username typed from memory is the commonest way one of these checks comes back\nempty \u2014 and an empty result is indistinguishable from \"nothing is wrong\", which is the worst\nanswer a diagnostic can give. The person operating the page may not know that Plex usernames are\nnot display names, or how a title is spelled in the library.\n\nDeliberately DB-only and unconditional: no Plex call, so it still populates when the server is\nunreachable, which is exactly when these checks are being used.", "operationId": "suggestions_api_support_suggestions_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Suggestions Api Support Suggestions Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Suggestions", "tags": [ "support" ] } }, "/api/support/surfaces": { "get": { "description": "Where every Shortlist row is ACTUALLY showing on the server, versus where it should be.\n\nThe gap this closes: rows are hidden from other people by share filters, but the server OWNER has\nno share filter (plex-safety rule 5), so the only thing keeping somebody else's row off the\nowner's Home screen is the row's own ``promotedToOwnHome`` flag. Nothing reported those flags, so\n\"the admin can see another user's row\" was uninvestigable (issue #75).\n\nTwo things are checked, and they are different in kind:\n\n* An INVARIANT \u2014 a per-person row that is not the owner's must never claim the owner's Home.\n No configuration makes that correct, so a violation is always a bug.\n* A CONSEQUENCE \u2014 the Recommended shelf is one flag per collection, and the owner has no filter,\n so a row set to show on friends' library shelves also appears on the OWNER's. That is a Plex\n limitation (see `RowSpec.show_friends_library`), so it is reported as an explanation, not a\n fault: the fix is a settings change, not a code change.", "operationId": "surfaces_api_support_surfaces_get", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Surfaces Api Support Surfaces Get", "type": "object" } } }, "description": "Successful Response" } }, "summary": "Surfaces", "tags": [ "support" ] } }, "/api/support/timeline": { "get": { "description": "Runs, jobs and audited events on one axis, newest first.\n\nRendered in BOTH local and UTC. The database stores UTC and the log prints local, so a timeline\nthat picked one would be misread against the other \u2014 which is precisely the mistake this is here\nto stop someone making.\n\n``user`` is a PLAIN default, not ``Query(default=\"\")``. `bundle` calls these handlers directly,\nwhich bypasses FastAPI's dependency injection \u2014 so a `Query(...)` default arrives as a\n`fastapi.params.Query` OBJECT, which is truthy and then blows up on `in`. That silently cost the\ndownloaded diagnostic its whole runs/jobs/events section on every install. FastAPI still parses\n`?user=` from the query string for a plain str parameter, so nothing is lost by dropping it.", "operationId": "timeline_api_support_timeline_get", "parameters": [ { "in": "query", "name": "user", "required": false, "schema": { "default": "", "title": "User", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Timeline Api Support Timeline Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Timeline", "tags": [ "support" ] } }, "/api/support/title": { "get": { "description": "Where one title stands for every person: watched record, episodes, whether it counts, delivery.\n\nThe tool this whole surface was built for. A run log records how MANY titles a watch read\nreturned and never WHICH, so \"is this in their watched set\" has been unanswerable from the\noutside \u2014 this is that missing column.", "operationId": "title_lookup_api_support_title_get", "parameters": [ { "in": "query", "name": "q", "required": true, "schema": { "maxLength": 200, "minLength": 1, "title": "Q", "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": true, "title": "Response Title Lookup Api Support Title Get", "type": "object" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Title Lookup", "tags": [ "support" ] } }, "/api/system/api-token": { "delete": { "description": "Revoke the API token \u2014 any script still using it starts getting 401s on the next call.", "operationId": "revoke_api_token_api_system_api_token_delete", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiTokenRevokedOut" } } }, "description": "Successful Response" } }, "summary": "Revoke Api Token", "tags": [ "system" ] }, "get": { "description": "The owner API token itself (decrypted, for the owner to reveal/copy \u2014 like Sonarr/Radarr's key),\nplus whether one exists and when it was made. Owner-gated; never exposed via GET /api/settings.", "operationId": "api_token_status_api_system_api_token_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiTokenStatusOut" } } }, "description": "Successful Response" } }, "summary": "Api Token Status", "tags": [ "system" ] }, "post": { "description": "Generate (or replace) the owner API token. Stored encrypted at rest; regenerating invalidates\nthe previous token immediately.", "operationId": "create_api_token_api_system_api_token_post", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiTokenCreatedOut" } } }, "description": "Successful Response" } }, "summary": "Create Api Token", "tags": [ "system" ] } }, "/api/system/backups": { "get": { "description": "List available DB backups, newest first.", "operationId": "get_backups_api_system_backups_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/BackupOut" }, "title": "Response Get Backups Api System Backups Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "Get Backups", "tags": [ "system" ] }, "post": { "description": "Take a manual backup now.", "operationId": "create_backup_api_system_backups_post", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BackupCreatedOut" } } }, "description": "Successful Response" } }, "summary": "Create Backup", "tags": [ "system" ] } }, "/api/system/backups/restore": { "delete": { "description": "Cancel the restore waiting for the next start. Nothing has been changed yet, so nothing is undone.", "operationId": "cancel_restore_endpoint_api_system_backups_restore_delete", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PendingRestoreOut" } } }, "description": "Successful Response" } }, "summary": "Cancel Restore Endpoint", "tags": [ "system" ] }, "get": { "description": "The restore waiting for the next start, so the owner can see it is still to come, or cancel it.", "operationId": "pending_restore_endpoint_api_system_backups_restore_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PendingRestoreOut" } } }, "description": "Successful Response" } }, "summary": "Pending Restore Endpoint", "tags": [ "system" ] }, "post": { "description": "Queue a restore from a named backup. It is applied when the app next starts.\n\nNot applied here: the running app has the database open, and swapping the file under its pooled\nconnections let the shutdown checkpoint write the old database back over the restored one, so the\nrestart this asks for undid the restore. `backups.apply_pending_restore` swaps it in at boot, takes the\npre-restore copy there (so it holds everything written until the restart), and audits it in the\ndatabase it restored. Until then it can be seen and cancelled (`GET`/`DELETE` below), and one left\nwaiting for over a day is not applied.\n\nA restore is not a neutral rollback: the database is what decides WHO MAY SEE WHAT. Restoring a\ncopy taken before a shared row's audience was narrowed puts the wider audience back, and the\nshared-exclude prune \u2014 the only un-hiding path Shortlist has \u2014 then removes the `label!=` excludes\nthat were hiding that row. That is correct for the config being restored, and it is exactly the\nkind of change an operator does not expect from a button labelled \"restore\".\n\nSo it is stated, in the response and in the audit trail (rule 10), rather than left to be\ndiscovered on someone's Home screen.", "operationId": "restore_backup_endpoint_api_system_backups_restore_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RestoreRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BackupRestoredOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Restore Backup Endpoint", "tags": [ "system" ] } }, "/api/system/debug": { "get": { "description": "A pasteable diagnostics bundle for bug reports: version, DB migration head, scheduler jobs,\nconnection status, and record counts. Deliberately plain text and secrets-free \u2014 every connection\nis reported as a yes/no, never a token or key (plex-safety rule 9).", "operationId": "debug_bundle_api_system_debug_get", "responses": { "200": { "content": { "text/plain": { "schema": { "type": "string" } } }, "description": "Successful Response" } }, "summary": "Debug Bundle", "tags": [ "system" ] } }, "/api/system/health": { "get": { "description": "Liveness only \u2014 this is the one unauthenticated endpoint, and Docker's HEALTHCHECK is its\nconsumer. It carries no version: an unauthenticated caller does not need to know which build to\nlook up advisories for. The UI reads it from `/system/version`, which is owner-gated.", "operationId": "health_api_system_health_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthOut" } } }, "description": "Successful Response" } }, "summary": "Health", "tags": [ "system" ] } }, "/api/system/image-provider": { "get": { "description": "Whether the configured AI provider can generate poster images (and a plain-English reason if\nnot) \u2014 so the row editor can enable/disable the \"Generate\" poster option honestly.", "operationId": "image_provider_api_system_image_provider_get", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageProviderOut" } } }, "description": "Successful Response" } }, "summary": "Image Provider", "tags": [ "system" ] } }, "/api/system/jobs": { "get": { "description": "Recent background jobs, newest first \u2014 the \"did that actually happen?\" answer.\n\nMaintenance work used to be fire-and-forget: it landed in the logs and the events table and\nnowhere an operator would look. Runs keep their own page; this is everything else.\n\n`kind` narrows it to one job type, which is how the Jobs page shows a single job's own history\nwithout pulling every other kind's rows down with it. `before_id` pages backwards \u2014 pass the id\nof the oldest job you already have.\n\n`status` narrows it to one outcome, and it exists because the Jobs page's \"N failed\" badge\ncounts every failed row in the table while the feed behind it could only fetch the newest N.\nMeasured on a real server: 8 failures, all `privacy.sync`, at ids 587-596 \u2014 the newest hundred\njobs started at id 680, so filtering a fetched page client-side answered \"8 failed\" with an\nempty list. A count over the whole table needs a filter over the whole table.\n\n`exclude_routine` also drops a finished job whose result says it was `quiet` \u2014 a scheduled privacy sync\nthat found nothing to change. And it drops the high-volume automatic kinds (`JobKind.routine`) unless they FAILED,\nand exists for the same reason `status` does: a client filter over a fetched page cannot work\nwhen the noise outnumbers the news. Measured on a 46-user server, `watch.reconcile` was 165 of\nthe 197 jobs queued in a day, so the newest 30 rows the header polls were almost all reconciles\nand nothing else could be seen behind them. The Jobs page does not pass it \u2014 that is where you\ngo to look at reconciles \u2014 and a failure is never dropped, because a reconcile that fails is the\nonly thing that would say a partial watch went uncredited.", "operationId": "list_jobs_api_system_jobs_get", "parameters": [ { "in": "query", "name": "limit", "required": false, "schema": { "default": 25, "maximum": 200, "minimum": 1, "title": "Limit", "type": "integer" } }, { "in": "query", "name": "kind", "required": false, "schema": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Kind" } }, { "in": "query", "name": "before_id", "required": false, "schema": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "title": "Before Id" } }, { "in": "query", "name": "status", "required": false, "schema": { "anyOf": [ { "enum": [ "queued", "running", "done", "failed" ], "type": "string" }, { "type": "null" } ], "title": "Status" } }, { "in": "query", "name": "exclude_routine", "required": false, "schema": { "default": false, "title": "Exclude Routine", "type": "boolean" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/JobOut" }, "title": "Response List Jobs Api System Jobs Get", "type": "array" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "List Jobs", "tags": [ "system" ] }, "post": { "description": "Queue a maintenance job and drain immediately, so pressing a button still feels instant.\n\nThe queue is the SAFETY NET, not a delay: if this attempt fails the job stays queued and the\nworker retries it with backoff, which is exactly what fire-and-forget could never do.", "operationId": "run_job_api_system_jobs_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RunJobRequest" } } }, "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JobRunOut" } } }, "description": "Successful Response" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } }, "description": "Validation Error" } }, "summary": "Run Job", "tags": [ "system" ] } }, "/api/system/jobs/catalog": { "get": { "description": "Every job Shortlist can run: what it does, when it next runs, and how it went last time.\n\nThe Jobs page is organised BY JOB, not by chronology \u2014 \"is the roster sync healthy?\" was\nunanswerable from a flat list of the last 25 rows mixing every kind together. Each entry\ncarries enough to render a card without a second request per kind.", "operationId": "jobs_catalog_api_system_jobs_catalog_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/JobCatalogEntryOut" }, "title": "Response Jobs Catalog Api System Jobs Catalog Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "Jobs Catalog", "tags": [ "system" ] } }, "/api/system/libraries": { "get": { "description": "The server's movie/show libraries, so the Rows editor can offer them as delivery targets.", "operationId": "libraries_api_system_libraries_get", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/LibraryOut" }, "title": "Response Libraries Api System Libraries Get", "type": "array" } } }, "description": "Successful Response" } }, "summary": "Libraries", "tags": [ "system" ] } }, "/api/system/libraries/{key}/collections": { "get": { "description": "A library's FOREIGN managed (orderable) collections \u2014 the candidate anchors for placing\nShortlist rows in the Recommended shelf that are not themselves ours.\n\nOur own collections are excluded, because a Shortlist row is never anchored by TITLE: it is one\ncollection PER PERSON, so a title names one account's copy and would place the row for that one\naccount and nobody else. Anchoring to another Shortlist row is done by row slug instead\n(`hub_anchor[library].row`), which the editor offers alongside this list \u2014 that is issue #81, and\nforty identical-looking \"Picked for You\" entries in this list was the symptom.\n\nExcluded by the invisible title MARKER, never by reading labels. `collection.labels` makes plexapi\nsilently re-read each collection and a read that comes back carrying no