Files
sofa/docs/public/openapi.json
T
2026-03-27 15:52:22 -04:00

8012 lines
249 KiB
JSON

{
"info": {
"title": "Sofa API",
"version": "0.2.0"
},
"servers": [
{
"url": "{protocol}://{host}:{port}/api/v1",
"variables": {
"protocol": {
"default": "http",
"enum": [
"http",
"https"
]
},
"host": {
"default": "localhost"
},
"port": {
"default": "3000"
}
}
}
],
"tags": [
{
"name": "Titles",
"description": "Movie and TV show metadata"
},
{
"name": "Tracking",
"description": "Watch tracking, ratings, and status management"
},
{
"name": "Library",
"description": "User library browsing and feeds"
},
{
"name": "Discover",
"description": "Search, trending, and content discovery"
},
{
"name": "People",
"description": "Cast and crew information"
},
{
"name": "Account",
"description": "User account and integrations"
},
{
"name": "System",
"description": "Server status and configuration"
},
{
"name": "Admin",
"description": "Server administration"
},
{
"name": "Imports",
"description": "Data import from external services"
}
],
"components": {
"securitySchemes": {
"session": {
"type": "apiKey",
"name": "better-auth.session_token",
"in": "cookie",
"description": "Better Auth session cookie"
}
},
"schemas": {
"Title": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Internal UUIDv7 identifier"
},
"tmdbId": {
"type": "number",
"description": "TMDB numeric ID"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type"
},
"title": {
"type": "string",
"description": "Display title (localized)"
},
"originalTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Original language title"
},
"overview": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Plot synopsis"
},
"releaseDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Theatrical release date (movies, ISO 8601)"
},
"firstAirDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "First air date (TV shows, ISO 8601)"
},
"posterPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Poster image path"
},
"posterThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the poster"
},
"backdropPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Backdrop image path"
},
"backdropThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the backdrop"
},
"popularity": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "TMDB popularity score"
},
"voteAverage": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Average user rating (0-10)"
},
"voteCount": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Total number of votes"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Production status (e.g. Released, Returning Series)"
},
"contentRating": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Content rating (e.g. PG-13, TV-MA)"
},
"imdbId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "IMDb title ID (e.g. tt0137523)"
},
"tvdbId": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "TVDB ID (TV shows only)"
},
"originalLanguage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Original language ISO 639-1 code (e.g. en)"
},
"runtimeMinutes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Runtime in minutes (movies only)"
},
"colorPalette": {
"anyOf": [
{
"type": "object",
"properties": {
"vibrant": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"darkVibrant": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"lightVibrant": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"muted": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"darkMuted": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"lightMuted": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"vibrant",
"darkVibrant",
"lightVibrant",
"muted",
"darkMuted",
"lightMuted"
],
"description": "Extracted color palette from the title poster image (CSS hex values)"
},
{
"type": "null"
}
]
},
"trailerVideoKey": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "YouTube video key for the trailer"
},
"genres": {
"type": "array",
"items": {
"type": "string"
},
"description": "Genre names"
}
},
"required": [
"id",
"tmdbId",
"type",
"title",
"originalTitle",
"overview",
"releaseDate",
"firstAirDate",
"posterPath",
"posterThumbHash",
"backdropPath",
"backdropThumbHash",
"popularity",
"voteAverage",
"voteCount",
"status",
"contentRating",
"imdbId",
"tvdbId",
"originalLanguage",
"runtimeMinutes",
"colorPalette",
"trailerVideoKey",
"genres"
],
"description": "A fully resolved movie or TV show from TMDB"
},
"Person": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Internal UUIDv7 identifier"
},
"tmdbId": {
"type": "number",
"description": "TMDB person ID"
},
"name": {
"type": "string",
"description": "Full name"
},
"biography": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Biography text"
},
"birthday": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Date of birth (ISO 8601)"
},
"deathday": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Date of death (ISO 8601)"
},
"placeOfBirth": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Place of birth"
},
"profilePath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Profile photo image path"
},
"profileThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the profile photo"
},
"knownForDepartment": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Primary department (e.g. Acting, Directing)"
},
"imdbId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "IMDb person ID (e.g. nm0000123)"
}
},
"required": [
"id",
"tmdbId",
"name",
"biography",
"birthday",
"deathday",
"placeOfBirth",
"profilePath",
"profileThumbHash",
"knownForDepartment",
"imdbId"
],
"description": "A person (actor, director, crew member) from TMDB"
},
"PersonCredit": {
"type": "object",
"properties": {
"titleId": {
"type": "string",
"description": "Internal title ID"
},
"tmdbId": {
"type": "number",
"description": "TMDB title ID"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type"
},
"title": {
"type": "string",
"description": "Title name"
},
"posterPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Poster image path"
},
"posterThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the poster"
},
"releaseDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Release date (ISO 8601)"
},
"firstAirDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "First air date (ISO 8601)"
},
"voteAverage": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Average rating (0-10)"
},
"character": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Character name (for acting credits)"
},
"department": {
"type": "string",
"description": "Department (e.g. Acting, Directing)"
},
"job": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Job title (for crew credits)"
}
},
"required": [
"titleId",
"tmdbId",
"type",
"title",
"posterPath",
"posterThumbHash",
"releaseDate",
"firstAirDate",
"voteAverage",
"character",
"department",
"job"
],
"description": "A person's credit in a movie or TV show"
},
"Episode": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Episode ID"
},
"episodeNumber": {
"type": "number",
"description": "Episode number within the season"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Episode title"
},
"overview": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Episode plot summary"
},
"stillPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Episode still image path"
},
"stillThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the still image"
},
"airDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Original air date (ISO 8601)"
},
"runtimeMinutes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Episode runtime in minutes"
}
},
"required": [
"id",
"episodeNumber",
"name",
"overview",
"stillPath",
"stillThumbHash",
"airDate",
"runtimeMinutes"
],
"description": "A single TV episode"
},
"Season": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Season ID"
},
"seasonNumber": {
"type": "number",
"description": "Season number (0 for specials)"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Season name"
},
"episodes": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Episode"
},
"description": "Episodes in this season"
}
},
"required": [
"id",
"seasonNumber",
"name",
"episodes"
],
"description": "A TV season with its episodes"
},
"CastMember": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Credit ID"
},
"personId": {
"type": "string",
"description": "Internal person ID"
},
"name": {
"type": "string",
"description": "Person's name"
},
"character": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Character name (for acting credits)"
},
"department": {
"type": "string",
"description": "Department (e.g. Acting, Directing)"
},
"job": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Job title (for crew credits)"
},
"displayOrder": {
"type": "number",
"description": "Sort order in the credits list"
},
"episodeCount": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Number of episodes (TV only)"
},
"profilePath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Profile photo image path"
},
"profileThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the profile photo"
},
"tmdbId": {
"type": "number",
"description": "TMDB person ID"
}
},
"required": [
"id",
"personId",
"name",
"character",
"department",
"job",
"displayOrder",
"episodeCount",
"profilePath",
"profileThumbHash",
"tmdbId"
],
"description": "A cast or crew member credit"
},
"BrowseItem": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Internal title ID"
},
"tmdbId": {
"type": "number",
"description": "TMDB numeric ID"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type"
},
"title": {
"type": "string",
"description": "Display title"
},
"posterPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Poster image path"
},
"posterThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the poster"
},
"releaseDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Release date (ISO 8601)"
},
"firstAirDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "First air date (ISO 8601)"
},
"voteAverage": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Average rating (0-10)"
}
},
"required": [
"id",
"tmdbId",
"type",
"title",
"posterPath",
"posterThumbHash",
"releaseDate",
"firstAirDate",
"voteAverage"
],
"description": "A TMDB title card used in browse/trending/popular lists"
},
"Recommendation": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Internal title ID"
},
"tmdbId": {
"type": "number",
"description": "TMDB numeric ID"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type"
},
"title": {
"type": "string",
"description": "Display title"
},
"posterPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Poster image path"
},
"posterThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the poster"
},
"releaseDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Release date (ISO 8601)"
},
"firstAirDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "First air date (ISO 8601)"
},
"voteAverage": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Average rating (0-10)"
}
},
"required": [
"id",
"tmdbId",
"type",
"title",
"posterPath",
"posterThumbHash",
"releaseDate",
"firstAirDate",
"voteAverage"
],
"description": "A recommended title"
},
"Integration": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Integration ID"
},
"provider": {
"type": "string",
"description": "Provider name (plex, jellyfin, etc.)"
},
"type": {
"enum": [
"webhook",
"list"
],
"type": "string",
"description": "Integration type: webhook (Plex/Jellyfin/Emby) or list (Sonarr/Radarr)"
},
"token": {
"type": "string",
"description": "Webhook authentication token"
},
"enabled": {
"type": "boolean",
"description": "Whether the integration is active"
},
"lastEventAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Last received event timestamp (ISO 8601)"
},
"createdAt": {
"type": "string",
"description": "When the integration was created (ISO 8601)"
}
},
"required": [
"id",
"provider",
"type",
"token",
"enabled",
"lastEventAt",
"createdAt"
],
"description": "A media server integration configuration"
},
"IntegrationEvent": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Event ID"
},
"eventType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Webhook event type"
},
"mediaType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Media type from the event"
},
"mediaTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Title from the event"
},
"status": {
"enum": [
"success",
"ignored",
"error"
],
"type": "string",
"description": "Event processing outcome"
},
"receivedAt": {
"type": "string",
"description": "When the event was received (ISO 8601)"
}
},
"required": [
"id",
"eventType",
"mediaType",
"mediaTitle",
"status",
"receivedAt"
],
"description": "A webhook or sync event from a media server"
},
"Backup": {
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "Backup filename on disk"
},
"sizeBytes": {
"type": "number",
"description": "Backup file size in bytes"
},
"createdAt": {
"type": "string",
"description": "When the backup was created (ISO 8601)"
},
"source": {
"enum": [
"manual",
"scheduled",
"pre-restore"
],
"type": "string",
"description": "How the backup was created: manual, scheduled, or automatic pre-restore"
}
},
"required": [
"filename",
"sizeBytes",
"createdAt",
"source"
],
"description": "A database backup file"
},
"Job": {
"type": "object",
"properties": {
"jobName": {
"type": "string",
"description": "Cron job identifier"
},
"cronPattern": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Cron expression (e.g. 0 2 * * *)"
},
"nextRunAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Next scheduled run (ISO 8601)"
},
"lastRunAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Last run start time (ISO 8601)"
},
"lastDurationMs": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Duration of last run in milliseconds"
},
"lastStatus": {
"anyOf": [
{
"enum": [
"running",
"success",
"error"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "Outcome of the last run"
},
"lastError": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Error message from the last failed run"
},
"isCurrentlyRunning": {
"type": "boolean",
"description": "Whether the job is currently executing"
},
"disabled": {
"type": "boolean",
"description": "Whether the job is disabled"
}
},
"required": [
"jobName",
"cronPattern",
"nextRunAt",
"lastRunAt",
"lastDurationMs",
"lastStatus",
"lastError",
"isCurrentlyRunning",
"disabled"
],
"description": "Status of a background cron job"
},
"SystemHealth": {
"type": "object",
"properties": {
"database": {
"type": "object",
"properties": {
"dbSizeBytes": {
"type": "number",
"description": "SQLite database file size"
},
"walSizeBytes": {
"type": "number",
"description": "WAL file size"
},
"titleCount": {
"type": "number",
"description": "Total titles in database"
},
"episodeCount": {
"type": "number",
"description": "Total episodes in database"
},
"userCount": {
"type": "number",
"description": "Total registered users"
}
},
"required": [
"dbSizeBytes",
"walSizeBytes",
"titleCount",
"episodeCount",
"userCount"
],
"description": "Database size and record counts"
},
"tmdb": {
"type": "object",
"properties": {
"connected": {
"type": "boolean",
"description": "Whether TMDB API is reachable"
},
"tokenValid": {
"type": "boolean",
"description": "Whether the API token is valid"
},
"tokenConfigured": {
"type": "boolean",
"description": "Whether a token is set"
},
"responseTimeMs": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "TMDB API response time in milliseconds"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Error message if connectivity check failed"
}
},
"required": [
"connected",
"tokenValid",
"tokenConfigured",
"responseTimeMs",
"error"
],
"description": "TMDB API connectivity status"
},
"jobs": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Job"
},
"description": "Status of all cron jobs"
},
"imageCache": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether image caching is enabled"
},
"totalSizeBytes": {
"type": "number",
"description": "Total cache size on disk"
},
"imageCount": {
"type": "number",
"description": "Total cached images"
},
"categories": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"count": {
"type": "number",
"description": "Images in this category"
},
"sizeBytes": {
"type": "number",
"description": "Category size on disk"
}
},
"required": [
"count",
"sizeBytes"
]
},
"description": "Breakdown by image category (posters, backdrops, etc.)"
}
},
"required": [
"enabled",
"totalSizeBytes",
"imageCount",
"categories"
],
"description": "Image cache statistics"
},
"backups": {
"type": "object",
"properties": {
"lastBackupAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Last backup timestamp (ISO 8601)"
},
"lastBackupAgeHours": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Hours since the last backup"
},
"backupCount": {
"type": "number",
"description": "Total backup files"
},
"totalSizeBytes": {
"type": "number",
"description": "Total size of all backups"
}
},
"required": [
"lastBackupAt",
"lastBackupAgeHours",
"backupCount",
"totalSizeBytes"
],
"description": "Backup status and size information"
},
"environment": {
"type": "object",
"properties": {
"dataDir": {
"type": "string",
"description": "Configured data directory path"
},
"dataDirWritable": {
"type": "boolean",
"description": "Whether the data directory is writable"
},
"envVars": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Environment variable name"
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Current value (sensitive values are masked)"
}
},
"required": [
"name",
"value"
]
},
"description": "Relevant environment variable statuses"
}
},
"required": [
"dataDir",
"dataDirWritable",
"envVars"
],
"description": "Server environment information"
},
"checkedAt": {
"type": "string",
"description": "When this health check was performed (ISO 8601)"
}
},
"required": [
"database",
"tmdb",
"jobs",
"imageCache",
"backups",
"environment",
"checkedAt"
],
"description": "Comprehensive system health report covering database, TMDB, jobs, cache, backups, and environment"
}
}
},
"openapi": "3.1.1",
"paths": {
"/titles/{id}": {
"get": {
"operationId": "titles.get",
"summary": "Get title details",
"description": "Fetch full title metadata including seasons, cast, and streaming availability. Imports from TMDB on first access if not yet cached locally.",
"tags": [
"Titles"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Internal UUIDv7 identifier"
}
}
],
"responses": {
"200": {
"description": "Full title with seasons, cast, and availability offers",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"title": {
"$ref": "#/components/schemas/Title"
},
"seasons": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Season"
},
"description": "TV seasons (empty for movies)"
},
"availability": {
"type": "array",
"items": {
"type": "object",
"properties": {
"platformId": {
"type": "string",
"description": "Platform ID"
},
"providerName": {
"type": "string",
"description": "Display name (e.g. Netflix, Hulu)"
},
"logoPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Provider logo image path"
},
"offerType": {
"type": "string",
"description": "Offer category: stream or purchase"
},
"watchUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Direct link to watch on this provider"
},
"isUserSubscribed": {
"type": "boolean",
"description": "Whether the user subscribes to this platform"
}
},
"required": [
"platformId",
"providerName",
"logoPath",
"offerType",
"watchUrl",
"isUserSubscribed"
],
"description": "A streaming availability offer from a provider"
},
"description": "Streaming availability offers"
},
"cast": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CastMember"
},
"description": "Cast and crew credits"
}
},
"required": [
"title",
"seasons",
"availability",
"cast"
],
"description": "Full title details with seasons, cast, and streaming availability"
}
}
}
},
"404": {
"description": "404",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "NOT_FOUND"
},
"status": {
"const": 404
},
"message": {
"type": "string",
"default": "Title not found"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "TITLE_NOT_FOUND"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/titles/{id}/similar": {
"get": {
"operationId": "titles.similar",
"summary": "Get similar titles",
"description": "Fetch similar titles based on locally cached recommendation data, along with the user's statuses for each.",
"tags": [
"Titles"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Internal UUIDv7 identifier"
}
}
],
"responses": {
"200": {
"description": "Similar titles with user statuses",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"recommendations": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Recommendation"
}
},
"userStatuses": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string"
},
"description": "Map of title ID to the user's display status"
}
},
"required": [
"recommendations",
"userStatuses"
],
"description": "Recommended titles with the user's statuses"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/tracking/watch": {
"post": {
"operationId": "tracking.watch",
"summary": "Mark items as watched",
"description": "Mark one or more items as watched. Use scope to indicate what the IDs refer to: movie (title IDs), episode (episode IDs), season (season IDs), or series (title IDs to mark all episodes).",
"tags": [
"Tracking"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"scope": {
"enum": [
"movie",
"episode",
"season",
"series"
],
"type": "string",
"description": "What the IDs refer to: movie title, episode, season, or entire TV series"
},
"ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
},
"description": "IDs to mark as watched"
}
},
"required": [
"scope",
"ids"
],
"description": "Mark one or more items as watched"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/tracking/unwatch": {
"post": {
"operationId": "tracking.unwatch",
"summary": "Remove watch records",
"description": "Remove watch records for one or more items. Use scope to indicate what the IDs refer to: movie, episode, season, or series.",
"tags": [
"Tracking"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"scope": {
"enum": [
"movie",
"episode",
"season",
"series"
],
"type": "string",
"description": "What the IDs refer to: movie title, episode, season, or entire TV series"
},
"ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
},
"description": "IDs to unwatch"
}
},
"required": [
"scope",
"ids"
],
"description": "Remove watch records for one or more items"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/tracking/titles/{id}/status": {
"put": {
"operationId": "tracking.updateStatus",
"summary": "Update tracking status",
"description": "Set the user's tracking status for a title. Use null to remove the title from the library entirely.",
"tags": [
"Tracking"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Title ID"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"anyOf": [
{
"enum": [
"watchlist"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "Set to watchlist to add to library, or null to remove. Other transitions happen via watch endpoints."
}
},
"required": [
"status"
],
"description": "Add a title to the user's watchlist or remove from library"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/tracking/titles/{id}/rating": {
"put": {
"operationId": "tracking.rate",
"summary": "Rate a title",
"description": "Set a 0-5 star rating for a title. Use 0 to clear the rating.",
"tags": [
"Tracking"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Title ID"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"stars": {
"type": "integer",
"minimum": 0,
"maximum": 5,
"description": "Star rating from 0 (clear) to 5"
}
},
"required": [
"stars"
],
"description": "Set or clear a star rating for a title"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/tracking/titles/{id}": {
"get": {
"operationId": "tracking.userInfo",
"summary": "Get user's tracking info for a title",
"description": "Fetch the current user's tracking status, rating, and watched episode IDs for a title.",
"tags": [
"Tracking"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Internal UUIDv7 identifier"
}
}
],
"responses": {
"200": {
"description": "User's status, rating, and list of watched episode IDs",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"anyOf": [
{
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "User's display status, or null if not in library"
},
"rating": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "User's star rating (0-5), or null if unrated"
},
"episodeWatches": {
"type": "array",
"items": {
"type": "string"
},
"description": "IDs of episodes the user has watched"
}
},
"required": [
"status",
"rating",
"episodeWatches"
],
"description": "The current user's tracking info for a title"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/tracking/stats": {
"get": {
"operationId": "tracking.stats",
"summary": "Get watch statistics",
"description": "Fetch the user's watch counts grouped by time period. Useful for rendering activity charts and dashboard counters.",
"tags": [
"Tracking"
],
"parameters": [
{
"name": "type",
"in": "query",
"required": true,
"schema": {
"enum": [
"movie",
"episode"
],
"type": "string",
"description": "What to count: movie watches or episode watches"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "period",
"in": "query",
"required": true,
"schema": {
"enum": [
"today",
"this_week",
"this_month",
"this_year"
],
"type": "string",
"description": "Time range for the histogram"
},
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Watch counts bucketed by time period",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"count": {
"type": "number",
"description": "Total watches in the selected period"
},
"history": {
"type": "array",
"items": {
"type": "object",
"properties": {
"bucket": {
"type": "string",
"description": "Time period label (e.g. date or week)"
},
"count": {
"type": "number",
"description": "Number of watches in this bucket"
}
},
"required": [
"bucket",
"count"
],
"description": "A single time-bucketed watch count"
},
"description": "Watch counts bucketed by time period"
}
},
"required": [
"count",
"history"
],
"description": "Watch history with time-bucketed counts"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/library": {
"get": {
"operationId": "library.list",
"summary": "List library with filters",
"description": "Fetch paginated, filtered, and sorted titles from the user's library. Supports filtering by status, type, genre, rating, year, content rating, and streaming availability.",
"tags": [
"Library"
],
"parameters": [
{
"name": "search",
"in": "query",
"schema": {
"type": "string",
"maxLength": 200,
"description": "Search within library by title name"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "statuses",
"in": "query",
"schema": {
"type": "array",
"items": {
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string"
},
"description": "Filter by display statuses (multi-select)"
},
"style": "deepObject",
"explode": true,
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "type",
"in": "query",
"schema": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Filter by media type"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "genreId",
"in": "query",
"schema": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Filter by TMDB genre ID"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "ratingMin",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "Minimum user star rating"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "ratingMax",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "Maximum user star rating"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "yearMin",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1900,
"maximum": 2100,
"description": "Minimum release year"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "yearMax",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1900,
"maximum": 2100,
"description": "Maximum release year"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "contentRating",
"in": "query",
"schema": {
"type": "string",
"description": "Content rating filter (e.g. PG-13, TV-MA)"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "onMyServices",
"in": "query",
"schema": {
"type": "boolean",
"description": "Only show titles available on the user's streaming services"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "sortBy",
"in": "query",
"schema": {
"enum": [
"title",
"added_at",
"release_date",
"popularity",
"user_rating",
"vote_average"
],
"type": "string",
"default": "added_at",
"description": "Sort field"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "sortDirection",
"in": "query",
"schema": {
"enum": [
"asc",
"desc"
],
"type": "string",
"default": "desc",
"description": "Sort direction"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "page",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 500,
"default": 1,
"description": "Page number (1-indexed)"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 20,
"description": "Results per page (1-100, default 20)"
},
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Filtered library items with user statuses and ratings",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Title ID"
},
"tmdbId": {
"type": "number",
"description": "TMDB numeric ID"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type"
},
"title": {
"type": "string",
"description": "Display title"
},
"posterPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Poster image path"
},
"posterThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the poster"
},
"releaseDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Release date (ISO 8601)"
},
"firstAirDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "First air date (ISO 8601)"
},
"voteAverage": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Average rating (0-10)"
},
"userStatus": {
"anyOf": [
{
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "User's display status"
},
"userRating": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "User's star rating (1-5), or null"
}
},
"required": [
"id",
"tmdbId",
"type",
"title",
"posterPath",
"posterThumbHash",
"releaseDate",
"firstAirDate",
"voteAverage",
"userStatus",
"userRating"
],
"description": "A library item with user status and rating"
}
},
"page": {
"type": "number",
"description": "Current page number"
},
"totalPages": {
"type": "number",
"description": "Total number of pages available"
},
"totalResults": {
"type": "number",
"description": "Total number of results across all pages"
}
},
"required": [
"items",
"page",
"totalPages",
"totalResults"
],
"description": "Filtered and sorted library titles"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/library/genres": {
"get": {
"operationId": "library.genres",
"summary": "List genres in user's library",
"description": "Get the distinct genres present in the user's library, ordered alphabetically. Used to populate the genre filter dropdown.",
"tags": [
"Library"
],
"responses": {
"200": {
"description": "Genres present in the library",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"genres": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "number"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
]
},
"description": "Genres present in the user's library"
}
},
"required": [
"genres"
],
"description": "Genres that exist in the user's library"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/library/stats": {
"get": {
"operationId": "library.stats",
"summary": "Get library statistics",
"description": "Fetch aggregate library counts: total titles and completed titles.",
"tags": [
"Library"
],
"responses": {
"200": {
"description": "Library size and completed count",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"size": {
"type": "number",
"description": "Total titles in the user's library"
},
"completed": {
"type": "number",
"description": "Total titles with completed status"
}
},
"required": [
"size",
"completed"
],
"description": "Aggregate library statistics"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/library/continue-watching": {
"get": {
"operationId": "library.continueWatching",
"summary": "Get continue watching list",
"description": "Fetch TV shows the user is currently watching, with the next unwatched episode and progress for each.",
"tags": [
"Library"
],
"responses": {
"200": {
"description": "In-progress shows with next episode and watch progress",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Title ID"
},
"title": {
"type": "string",
"description": "Display title"
},
"backdropPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Backdrop image path"
},
"backdropThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the backdrop"
}
},
"required": [
"id",
"title",
"backdropPath",
"backdropThumbHash"
]
},
"nextEpisode": {
"anyOf": [
{
"type": "object",
"properties": {
"seasonNumber": {
"type": "number",
"description": "Season number"
},
"episodeNumber": {
"type": "number",
"description": "Episode number"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Episode title"
},
"stillPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Episode still image path"
},
"stillThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for the still"
}
},
"required": [
"seasonNumber",
"episodeNumber",
"name",
"stillPath",
"stillThumbHash"
]
},
{
"type": "null"
}
],
"description": "Next unwatched episode, or null if all caught up"
},
"totalEpisodes": {
"type": "number",
"description": "Total episodes across all seasons"
},
"watchedEpisodes": {
"type": "number",
"description": "Episodes the user has watched"
}
},
"required": [
"title",
"nextEpisode",
"totalEpisodes",
"watchedEpisodes"
],
"description": "An in-progress show with watch progress"
}
}
},
"required": [
"items"
],
"description": "TV shows the user is currently watching with next episode info"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/library/upcoming": {
"get": {
"operationId": "library.upcoming",
"summary": "Get upcoming episodes and movies",
"description": "Fetch upcoming episodes and movie releases for titles in the user's library, sorted by date. Supports cursor-based pagination.",
"tags": [
"Library"
],
"parameters": [
{
"name": "days",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 90,
"default": 90,
"description": "How many days into the future to look"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 50,
"default": 20,
"description": "Maximum items per page"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "cursor",
"in": "query",
"schema": {
"type": "string",
"description": "Pagination cursor"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "mediaType",
"in": "query",
"schema": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Filter to only movies or only TV episodes"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "statusFilter",
"in": "query",
"schema": {
"type": "array",
"items": {
"enum": [
"watching",
"watchlist"
],
"type": "string"
},
"description": "Filter by user tracking status"
},
"style": "deepObject",
"explode": true,
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Upcoming items sorted by date with streaming info",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"episodeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Episode ID (TV only, null for movies and collapsed batches)"
},
"titleId": {
"type": "string",
"description": "Internal title ID"
},
"titleName": {
"type": "string",
"description": "Display title"
},
"titleType": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type"
},
"posterPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Poster image path"
},
"posterThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder"
},
"backdropPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Backdrop image path"
},
"backdropThumbHash": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ThumbHash blur placeholder for backdrop"
},
"seasonNumber": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Season number (TV only)"
},
"episodeNumber": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Episode number (TV only)"
},
"episodeName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Episode title (TV only)"
},
"episodeCount": {
"type": "number",
"description": "Number of episodes (1 for single, >1 for collapsed batch drops)"
},
"date": {
"type": "string",
"description": "Air date or release date (YYYY-MM-DD)"
},
"userStatus": {
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string",
"description": "User's display status"
},
"isNewSeason": {
"type": "boolean",
"description": "Whether this is a new season for a completed show"
},
"streamingProvider": {
"anyOf": [
{
"type": "object",
"properties": {
"platformId": {
"type": "string"
},
"providerName": {
"type": "string"
},
"logoPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"platformId",
"providerName",
"logoPath"
]
},
{
"type": "null"
}
],
"description": "Primary streaming provider, or null"
}
},
"required": [
"episodeId",
"titleId",
"titleName",
"titleType",
"posterPath",
"posterThumbHash",
"backdropPath",
"backdropThumbHash",
"seasonNumber",
"episodeNumber",
"episodeName",
"episodeCount",
"date",
"userStatus",
"isNewSeason",
"streamingProvider"
],
"description": "An upcoming episode or movie release"
}
},
"nextCursor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Cursor for the next page, or null if no more items"
}
},
"required": [
"items",
"nextCursor"
],
"description": "Upcoming episodes and movie releases for tracked titles"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/discover/trending": {
"get": {
"operationId": "discover.trending",
"summary": "Get trending titles",
"description": "Fetch today's trending movies and/or TV shows from TMDB, including a featured hero title and the user's statuses.",
"tags": [
"Discover"
],
"parameters": [
{
"name": "type",
"in": "query",
"required": true,
"schema": {
"enum": [
"all",
"movie",
"tv"
],
"type": "string",
"description": "Trending category: all, movie, or tv"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 500,
"default": 1,
"description": "Page number (1-indexed)"
},
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Trending items, hero spotlight, and user statuses",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BrowseItem"
},
"description": "Trending titles"
},
"hero": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Internal title ID"
},
"tmdbId": {
"type": "number",
"description": "TMDB numeric ID"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type"
},
"title": {
"type": "string",
"description": "Display title"
},
"overview": {
"type": "string",
"description": "Plot synopsis"
},
"backdropPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Backdrop image path"
},
"voteAverage": {
"type": "number",
"description": "Average rating (0-10)"
}
},
"required": [
"id",
"tmdbId",
"type",
"title",
"overview",
"backdropPath",
"voteAverage"
]
},
{
"type": "null"
}
],
"description": "Featured hero title for the spotlight banner"
},
"userStatuses": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string"
},
"description": "Map of title ID to the user's display status"
},
"episodeProgress": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"watched": {
"type": "number"
},
"total": {
"type": "number"
}
},
"required": [
"watched",
"total"
]
},
"description": "Map of title ID to episode watch progress"
},
"page": {
"type": "number",
"description": "Current page number"
},
"totalPages": {
"type": "number",
"description": "Total number of pages available"
},
"totalResults": {
"type": "number",
"description": "Total number of results across all pages"
}
},
"required": [
"items",
"hero",
"userStatuses",
"episodeProgress",
"page",
"totalPages",
"totalResults"
],
"description": "Trending titles with hero spotlight and user statuses"
}
}
}
},
"412": {
"description": "412",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "PRECONDITION_FAILED"
},
"status": {
"const": 412
},
"message": {
"type": "string",
"default": "TMDB API key is not configured"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "TMDB_NOT_CONFIGURED"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/discover/popular": {
"get": {
"operationId": "discover.popular",
"summary": "Get popular titles",
"description": "Fetch currently popular movies or TV shows from TMDB with the user's tracking statuses.",
"tags": [
"Discover"
],
"parameters": [
{
"name": "type",
"in": "query",
"required": true,
"schema": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type filter"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 500,
"default": 1,
"description": "Page number (1-indexed)"
},
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Popular items with user statuses",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BrowseItem"
}
},
"userStatuses": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string"
},
"description": "Map of title ID to the user's display status"
},
"episodeProgress": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"watched": {
"type": "number"
},
"total": {
"type": "number"
}
},
"required": [
"watched",
"total"
]
},
"description": "Map of title ID to episode watch progress"
},
"page": {
"type": "number",
"description": "Current page number"
},
"totalPages": {
"type": "number",
"description": "Total number of pages available"
},
"totalResults": {
"type": "number",
"description": "Total number of results across all pages"
}
},
"required": [
"items",
"userStatuses",
"episodeProgress",
"page",
"totalPages",
"totalResults"
],
"description": "Browse results with user tracking statuses and episode progress"
}
}
}
},
"412": {
"description": "412",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "PRECONDITION_FAILED"
},
"status": {
"const": 412
},
"message": {
"type": "string",
"default": "TMDB API key is not configured"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "TMDB_NOT_CONFIGURED"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/discover/search": {
"get": {
"operationId": "discover.search",
"summary": "Search movies, TV shows, and people",
"description": "Full-text search across movies, TV shows, and people via TMDB. Optionally filter by media type.",
"tags": [
"Discover"
],
"parameters": [
{
"name": "query",
"in": "query",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Search query text"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "type",
"in": "query",
"required": false,
"schema": {
"enum": [
"movie",
"tv",
"person"
],
"type": "string",
"description": "Optional type filter to narrow results"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 500,
"default": 1,
"description": "Page number (1-indexed)"
},
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Search results with metadata",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Internal title ID (present for movie/tv results, absent for people)"
},
"tmdbId": {
"type": "number",
"description": "TMDB numeric ID"
},
"type": {
"enum": [
"movie",
"tv",
"person"
],
"type": "string",
"description": "Result type"
},
"title": {
"type": "string",
"description": "Title or person name"
},
"overview": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Plot summary or null for people"
},
"posterPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Poster image path (movies/TV)"
},
"profilePath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Profile photo path (people)"
},
"releaseDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Release date (ISO 8601)"
},
"popularity": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "TMDB popularity score"
},
"voteAverage": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Average rating (0-10)"
},
"knownForDepartment": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Primary department (people only)"
},
"knownFor": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
],
"description": "Notable works (people only, up to 3 titles)"
}
},
"required": [
"tmdbId",
"type",
"title",
"overview",
"posterPath",
"profilePath",
"releaseDate",
"popularity",
"voteAverage",
"knownForDepartment",
"knownFor"
],
"description": "A search result (movie, TV show, or person)"
}
},
"page": {
"type": "number",
"description": "Current page number"
},
"totalPages": {
"type": "number",
"description": "Total number of pages available"
},
"totalResults": {
"type": "number",
"description": "Total number of results across all pages"
}
},
"required": [
"results",
"page",
"totalPages",
"totalResults"
],
"description": "Search results from TMDB"
}
}
}
},
"412": {
"description": "412",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "PRECONDITION_FAILED"
},
"status": {
"const": 412
},
"message": {
"type": "string",
"default": "TMDB API key is not configured"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "TMDB_NOT_CONFIGURED"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/discover/browse": {
"get": {
"operationId": "discover.browse",
"summary": "Browse titles with filters",
"description": "Browse movies or TV shows filtered by genre, sorted by popularity. Returns user statuses and episode progress.",
"tags": [
"Discover"
],
"parameters": [
{
"name": "type",
"in": "query",
"required": true,
"schema": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type to discover"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "genreId",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "TMDB genre ID to filter by"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "yearMin",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1900,
"maximum": 2100,
"description": "Minimum release year"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "yearMax",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1900,
"maximum": 2100,
"description": "Maximum release year"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "ratingMin",
"in": "query",
"required": false,
"schema": {
"type": "number",
"minimum": 0,
"maximum": 10,
"description": "Minimum TMDB vote average"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "sortBy",
"in": "query",
"required": false,
"schema": {
"enum": [
"popularity.desc",
"vote_average.desc",
"primary_release_date.desc",
"primary_release_date.asc"
],
"type": "string",
"description": "Sort order for results"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "language",
"in": "query",
"required": false,
"schema": {
"type": "string",
"minLength": 2,
"maxLength": 2,
"pattern": "^[a-z]{2}$",
"description": "ISO 639-1 original language code"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "platformId",
"in": "query",
"required": false,
"schema": {
"type": "string",
"description": "Platform ID to filter by"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 500,
"default": 1,
"description": "Page number (1-indexed)"
},
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Filtered titles with user statuses",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BrowseItem"
}
},
"userStatuses": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string"
},
"description": "Map of title ID to the user's display status"
},
"episodeProgress": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"watched": {
"type": "number"
},
"total": {
"type": "number"
}
},
"required": [
"watched",
"total"
]
},
"description": "Map of title ID to episode watch progress"
},
"page": {
"type": "number",
"description": "Current page number"
},
"totalPages": {
"type": "number",
"description": "Total number of pages available"
},
"totalResults": {
"type": "number",
"description": "Total number of results across all pages"
}
},
"required": [
"items",
"userStatuses",
"episodeProgress",
"page",
"totalPages",
"totalResults"
],
"description": "Browse results with user tracking statuses and episode progress"
}
}
}
},
"412": {
"description": "412",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "PRECONDITION_FAILED"
},
"status": {
"const": 412
},
"message": {
"type": "string",
"default": "TMDB API key is not configured"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "TMDB_NOT_CONFIGURED"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/discover/genres": {
"get": {
"operationId": "discover.genres",
"summary": "List genres",
"description": "Fetch the list of TMDB genres for movies or TV shows. Used to populate genre filters for discovery.",
"tags": [
"Discover"
],
"parameters": [
{
"name": "type",
"in": "query",
"required": true,
"schema": {
"enum": [
"movie",
"tv"
],
"type": "string",
"description": "Media type filter"
},
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Genre ID/name pairs",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"genres": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "TMDB genre ID"
},
"name": {
"type": "string",
"description": "Genre display name"
}
},
"required": [
"id",
"name"
]
}
}
},
"required": [
"genres"
],
"description": "Available genres for filtering"
}
}
}
},
"412": {
"description": "412",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "PRECONDITION_FAILED"
},
"status": {
"const": 412
},
"message": {
"type": "string",
"default": "TMDB API key is not configured"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "TMDB_NOT_CONFIGURED"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/discover/platforms": {
"get": {
"operationId": "discover.platforms",
"summary": "List available streaming platforms",
"description": "Fetch all available streaming platforms, ordered by popularity. Includes subscription status for filtering.",
"tags": [
"Discover"
],
"responses": {
"200": {
"description": "Platform list with logos and metadata",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"platforms": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Platform ID"
},
"name": {
"type": "string",
"description": "Display name"
},
"tmdbProviderIds": {
"type": "array",
"items": {
"type": "number"
},
"description": "TMDB provider IDs mapped to this platform"
},
"logoPath": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Logo image path"
},
"isSubscription": {
"type": "boolean",
"description": "True for subscription services, false for purchase/rental stores"
}
},
"required": [
"id",
"name",
"tmdbProviderIds",
"logoPath",
"isSubscription"
],
"description": "A streaming platform"
}
}
},
"required": [
"platforms"
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/discover/recommendations": {
"get": {
"operationId": "discover.recommendations",
"summary": "Get personalized recommendations",
"description": "Fetch personalized title recommendations based on the user's library and watch history.",
"tags": [
"Discover"
],
"responses": {
"200": {
"description": "Recommended titles",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Recommendation"
}
}
},
"required": [
"items"
],
"description": "Personalized title recommendations based on the user's library"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/people/{id}": {
"get": {
"operationId": "people.get",
"summary": "Get person details",
"description": "Fetch a person's profile and paginated filmography. Imports from TMDB on first access if not yet cached locally.",
"tags": [
"People"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Internal UUIDv7 identifier"
}
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 500,
"default": 1,
"description": "Page number (1-indexed)"
},
"allowEmptyValue": true,
"allowReserved": true
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 20,
"description": "Results per page (1-100, default 20)"
},
"allowEmptyValue": true,
"allowReserved": true
}
],
"responses": {
"200": {
"description": "Person profile, paginated filmography credits, and user statuses for their titles",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"person": {
"$ref": "#/components/schemas/Person"
},
"filmography": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PersonCredit"
},
"description": "Credits for this person (paginated)"
},
"userStatuses": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"enum": [
"in_watchlist",
"watching",
"caught_up",
"completed"
],
"type": "string"
},
"description": "Map of title ID to the user's display status"
},
"page": {
"type": "number",
"description": "Current page number"
},
"totalPages": {
"type": "number",
"description": "Total number of pages available"
},
"totalResults": {
"type": "number",
"description": "Total number of results across all pages"
}
},
"required": [
"person",
"filmography",
"userStatuses",
"page",
"totalPages",
"totalResults"
],
"description": "Person profile with paginated filmography and user's statuses for their titles"
}
}
}
},
"404": {
"description": "404",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "NOT_FOUND"
},
"status": {
"const": 404
},
"message": {
"type": "string",
"default": "Person not found"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "PERSON_NOT_FOUND"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/account/name": {
"put": {
"operationId": "account.updateName",
"summary": "Update display name",
"description": "Change the current user's display name.",
"tags": [
"Account"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"description": "New display name"
}
},
"required": [
"name"
]
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/account/avatar": {
"post": {
"operationId": "account.uploadAvatar",
"summary": "Upload avatar",
"description": "Upload a new profile avatar image. Accepts JPEG, PNG, WebP, or GIF up to 2 MB.",
"tags": [
"Account"
],
"requestBody": {
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"file": {
"type": "string",
"format": "binary"
}
},
"required": [
"file"
]
}
}
}
},
"responses": {
"200": {
"description": "URL of the uploaded avatar image",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"imageUrl": {
"type": "string",
"description": "URL of the uploaded avatar image"
}
},
"required": [
"imageUrl"
]
}
}
}
}
},
"security": [
{
"session": []
}
]
},
"delete": {
"operationId": "account.removeAvatar",
"summary": "Remove avatar",
"description": "Delete the current user's profile avatar, reverting to the default.",
"tags": [
"Account"
],
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/account/platforms": {
"get": {
"operationId": "account.platforms",
"summary": "Get user's streaming platforms",
"description": "Fetch the current user's subscribed streaming platform IDs.",
"tags": [
"Account"
],
"responses": {
"200": {
"description": "List of platform IDs",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"platformIds": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"platformIds"
]
}
}
}
}
},
"security": [
{
"session": []
}
]
},
"put": {
"operationId": "account.updatePlatforms",
"summary": "Update streaming platforms",
"description": "Set the current user's subscribed streaming platforms.",
"tags": [
"Account"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"platformIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of platform IDs the user subscribes to"
}
},
"required": [
"platformIds"
]
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/account/integrations": {
"get": {
"operationId": "account.integrations.list",
"summary": "List integrations",
"description": "Fetch all configured media server integrations for the current user, including recent webhook/sync events for each.",
"tags": [
"Account"
],
"responses": {
"200": {
"description": "Integrations with their recent events",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"integrations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Integration ID"
},
"provider": {
"type": "string",
"description": "Provider name (plex, jellyfin, etc.)"
},
"type": {
"enum": [
"webhook",
"list"
],
"type": "string",
"description": "Integration type: webhook (Plex/Jellyfin/Emby) or list (Sonarr/Radarr)"
},
"token": {
"type": "string",
"description": "Webhook authentication token"
},
"enabled": {
"type": "boolean",
"description": "Whether the integration is active"
},
"lastEventAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Last received event timestamp (ISO 8601)"
},
"createdAt": {
"type": "string",
"description": "When the integration was created (ISO 8601)"
},
"recentEvents": {
"type": "array",
"items": {
"$ref": "#/components/schemas/IntegrationEvent"
},
"description": "Last 10 events for this integration"
}
},
"required": [
"id",
"provider",
"type",
"token",
"enabled",
"lastEventAt",
"createdAt",
"recentEvents"
]
}
}
},
"required": [
"integrations"
],
"description": "All integrations with their recent events"
}
}
}
}
},
"security": [
{
"session": []
}
]
},
"post": {
"operationId": "account.integrations.create",
"summary": "Create or update integration",
"description": "Create a new media server integration or update an existing one. Generates a unique webhook token for the provider.",
"tags": [
"Account"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"provider": {
"enum": [
"plex",
"jellyfin",
"emby",
"sonarr",
"radarr"
],
"type": "string",
"description": "Media server provider to integrate"
},
"enabled": {
"type": "boolean",
"description": "Whether the integration starts enabled (default: true)"
}
},
"required": [
"provider"
],
"description": "Create a new media server integration"
}
}
}
},
"responses": {
"200": {
"description": "Created or updated integration with token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Integration"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/account/integrations/{provider}": {
"delete": {
"operationId": "account.integrations.delete",
"summary": "Delete integration",
"description": "Remove a media server integration and all its event history.",
"tags": [
"Account"
],
"parameters": [
{
"name": "provider",
"in": "path",
"required": true,
"schema": {
"enum": [
"plex",
"jellyfin",
"emby",
"sonarr",
"radarr"
],
"type": "string",
"description": "Media server provider type"
}
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/account/integrations/{provider}/regenerate-token": {
"post": {
"operationId": "account.integrations.regenerateToken",
"summary": "Regenerate webhook token",
"description": "Generate a new webhook token for an integration. The old token is immediately invalidated.",
"tags": [
"Account"
],
"parameters": [
{
"name": "provider",
"in": "path",
"required": true,
"schema": {
"enum": [
"plex",
"jellyfin",
"emby",
"sonarr",
"radarr"
],
"type": "string",
"description": "Media server provider type"
}
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "Integration with new token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Integration"
}
}
}
},
"404": {
"description": "404",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "NOT_FOUND"
},
"status": {
"const": 404
},
"message": {
"type": "string",
"default": "Integration not found"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "INTEGRATION_NOT_FOUND"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/system/public-info": {
"get": {
"operationId": "system.publicInfo",
"summary": "Get public instance info",
"description": "Fetch public information about this Sofa instance including authentication configuration. Does not require authentication.",
"tags": [
"System"
],
"responses": {
"200": {
"description": "Instance configuration, auth settings, and status",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"instanceId": {
"type": "string",
"description": "Unique instance identifier"
},
"tmdbConfigured": {
"type": "boolean",
"description": "Whether TMDB API is configured"
},
"userCount": {
"type": "number",
"description": "Number of registered users"
},
"registrationOpen": {
"type": "boolean",
"description": "Whether new user registration is open"
},
"posterUrls": {
"type": "array",
"items": {
"type": "string"
},
"description": "Poster image URLs for the login screen collage"
},
"oidcEnabled": {
"type": "boolean",
"description": "Whether OIDC/SSO login is available"
},
"oidcProviderName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Display name of the OIDC provider (e.g. Authelia, Keycloak)"
},
"passwordLoginDisabled": {
"type": "boolean",
"description": "Whether password-based login is disabled"
}
},
"required": [
"instanceId",
"tmdbConfigured",
"userCount",
"registrationOpen",
"posterUrls",
"oidcEnabled",
"oidcProviderName",
"passwordLoginDisabled"
],
"description": "Public instance information and authentication configuration"
}
}
}
}
}
}
},
"/system/status": {
"get": {
"operationId": "system.status",
"summary": "Get internal system config",
"description": "Returns internal configuration such as the public API URL. Requires authentication.",
"tags": [
"System"
],
"responses": {
"200": {
"description": "Internal system configuration",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"publicApiUrl": {
"type": "string",
"description": "Base URL of the centralized public API"
}
},
"required": [
"publicApiUrl"
],
"description": "Internal system configuration for authenticated clients"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/settings": {
"get": {
"operationId": "admin.settings.get",
"summary": "Get admin settings",
"description": "Fetch all admin-configurable settings: registration, update checks, and telemetry.",
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Current admin settings",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"registration": {
"type": "object",
"properties": {
"open": {
"type": "boolean",
"description": "Whether new user registration is open"
}
},
"required": [
"open"
]
},
"updateCheck": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether automatic update checks are enabled"
},
"updateAvailable": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether a newer version is available"
},
"currentVersion": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Currently running version"
},
"latestVersion": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Latest available version"
},
"releaseUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL to the latest release page"
},
"lastCheckedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "When the last check was performed (ISO 8601)"
}
},
"required": [
"enabled",
"updateAvailable",
"currentVersion",
"latestVersion",
"releaseUrl",
"lastCheckedAt"
]
},
"telemetry": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether anonymous telemetry is enabled"
},
"lastReportedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO 8601 timestamp of the last telemetry report"
}
},
"required": [
"enabled",
"lastReportedAt"
]
}
},
"required": [
"registration",
"updateCheck",
"telemetry"
],
"description": "Combined admin settings for registration, update checks, and telemetry"
}
}
}
}
},
"security": [
{
"session": []
}
]
},
"patch": {
"operationId": "admin.settings.update",
"summary": "Update admin settings",
"description": "Partially update admin settings. Only provided sections are changed; omitted sections keep their current values.",
"tags": [
"Admin"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"registration": {
"type": "object",
"properties": {
"open": {
"type": "boolean",
"description": "Whether new user registration is allowed"
}
},
"required": [
"open"
]
},
"updateCheck": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether automatic update checks are enabled"
}
},
"required": [
"enabled"
]
},
"telemetry": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether anonymous telemetry is enabled"
}
},
"required": [
"enabled"
]
}
},
"description": "Partial update to admin settings"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/backups": {
"get": {
"operationId": "admin.backups.list",
"summary": "List backups",
"description": "Fetch all database backups with their sizes and creation times.",
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Available backups",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"backups": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Backup"
}
}
},
"required": [
"backups"
],
"description": "All available database backups"
}
}
}
}
},
"security": [
{
"session": []
}
]
},
"post": {
"operationId": "admin.backups.create",
"summary": "Create backup",
"description": "Create a new manual database backup. The backup is tagged as a manual backup.",
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Created backup metadata",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Backup"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/backups/{filename}": {
"delete": {
"operationId": "admin.backups.delete",
"summary": "Delete backup",
"description": "Permanently delete a database backup file by filename.",
"tags": [
"Admin"
],
"parameters": [
{
"name": "filename",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Backup filename"
}
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "400",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "BAD_REQUEST"
},
"status": {
"const": 400
},
"message": {
"type": "string",
"default": "Failed to delete backup"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "BACKUP_DELETE_FAILED"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
},
"404": {
"description": "404",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "NOT_FOUND"
},
"status": {
"const": 404
},
"message": {
"type": "string",
"default": "Backup not found"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "BACKUP_NOT_FOUND"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/backups/restore": {
"post": {
"operationId": "admin.backups.restore",
"summary": "Restore from backup",
"description": "Upload a database backup file and restore it. A pre-restore backup is automatically created before overwriting the current database.",
"tags": [
"Admin"
],
"requestBody": {
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"file": {
"type": "string",
"format": "binary"
}
},
"required": [
"file"
]
}
}
}
},
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "400",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "BAD_REQUEST"
},
"status": {
"const": 400
},
"message": {
"type": "string",
"default": "Backup restoration failed"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "BACKUP_RESTORE_FAILED"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/backups/schedule": {
"get": {
"operationId": "admin.backups.schedule",
"summary": "Get backup schedule",
"description": "Fetch the current automated backup schedule configuration.",
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Backup schedule settings",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether scheduled backups are enabled"
},
"maxRetention": {
"type": "number",
"description": "Maximum backups to keep (0 = unlimited)"
},
"frequency": {
"enum": [
"6h",
"12h",
"1d",
"7d"
],
"type": "string",
"description": "Backup interval"
},
"time": {
"type": "string",
"description": "Scheduled time (HH:MM, 24-hour format)"
},
"dayOfWeek": {
"type": "number",
"description": "Day of week for weekly backups (0 = Sunday)"
}
},
"required": [
"enabled",
"maxRetention",
"frequency",
"time",
"dayOfWeek"
],
"description": "Automated backup schedule configuration"
}
}
}
}
},
"security": [
{
"session": []
}
]
},
"put": {
"operationId": "admin.backups.updateSchedule",
"summary": "Update backup schedule",
"description": "Update the automated backup schedule. Only provided fields are changed; omitted fields keep their current values.",
"tags": [
"Admin"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable or disable scheduled backups"
},
"frequency": {
"enum": [
"6h",
"12h",
"1d",
"7d"
],
"type": "string",
"description": "Backup interval"
},
"time": {
"type": "string",
"pattern": "^\\d{2}:\\d{2}$",
"description": "Time of day to run backups (HH:MM, 24-hour format)"
},
"dayOfWeek": {
"type": "integer",
"minimum": 0,
"maximum": 6,
"description": "Day of week for weekly backups (0 = Sunday, 6 = Saturday)"
},
"maxRetention": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Maximum number of backups to keep (0 = unlimited, 1-30 otherwise)"
}
},
"description": "Partial update to the automated backup schedule"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/jobs/trigger": {
"post": {
"operationId": "admin.triggerJob",
"summary": "Trigger cron job",
"description": "Manually trigger a background cron job by name. The job runs asynchronously.",
"tags": [
"Admin"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"enum": [
"scheduledBackup",
"nightlyRefreshLibrary",
"refreshAvailability",
"refreshRecommendations",
"refreshTvChildren",
"cacheImages",
"refreshCredits",
"updateCheck",
"telemetryReport"
],
"type": "string",
"description": "Cron job to trigger"
}
},
"required": [
"name"
],
"description": "Specify which background job to trigger manually"
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"ok": {
"const": true,
"description": "Always true on success"
}
},
"required": [
"ok"
]
}
}
}
},
"404": {
"description": "404",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "NOT_FOUND"
},
"status": {
"const": 404
},
"message": {
"type": "string",
"default": "Job not found"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "JOB_NOT_FOUND"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/cache/purge-metadata": {
"post": {
"operationId": "admin.purgeMetadataCache",
"summary": "Purge metadata cache",
"description": "Delete un-enriched stub titles not in any user's library and clean up orphaned person records.",
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Counts of deleted titles and persons",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"deletedTitles": {
"type": "number",
"description": "Number of un-enriched stub titles deleted"
},
"deletedPersons": {
"type": "number",
"description": "Number of orphaned person records deleted"
}
},
"required": [
"deletedTitles",
"deletedPersons"
],
"description": "Result of purging un-enriched metadata from the database"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/cache/purge-images": {
"post": {
"operationId": "admin.purgeImageCache",
"summary": "Purge image cache",
"description": "Delete all cached TMDB images from disk. Images will be re-downloaded on demand.",
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Count of deleted files and bytes freed",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"deletedFiles": {
"type": "number",
"description": "Number of image files deleted from disk"
},
"freedBytes": {
"type": "number",
"description": "Total bytes freed from disk"
}
},
"required": [
"deletedFiles",
"freedBytes"
],
"description": "Result of purging the image cache from disk"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/admin/system-health": {
"get": {
"operationId": "admin.systemHealth",
"summary": "Get system health report",
"description": "Comprehensive health check covering database, TMDB connectivity, cron jobs, image cache, backups, and environment.",
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Full system health report",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SystemHealth"
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/imports/parse-file": {
"post": {
"operationId": "imports.parseFile",
"summary": "Parse import file",
"description": "Upload and parse an export file from Trakt, Simkl, Letterboxd, or Sofa. Returns a preview of items found without importing anything.",
"tags": [
"Imports"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"file": {
"type": "string",
"contentMediaType": "*/*"
}
},
"required": [
"source",
"file"
]
}
},
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"file": {
"type": "string",
"contentMediaType": "*/*"
}
},
"required": [
"source",
"file"
]
}
}
}
},
"responses": {
"200": {
"description": "Preview of importable items with counts",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"movies": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"watchedAt": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp"
},
"watchedOn": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD date-only"
}
},
"required": [
"title"
]
}
},
"episodes": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"showTmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"showTitle": {
"type": "string"
},
"year": {
"type": "number"
},
"seasonNumber": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"episodeNumber": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"watchedAt": {
"type": "string",
"format": "date-time"
},
"watchedOn": {
"type": "string",
"format": "date"
}
},
"required": [
"seasonNumber",
"episodeNumber"
]
}
},
"watchlist": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string"
},
"status": {
"enum": [
"watchlist",
"in_progress",
"completed"
],
"type": "string",
"description": "Library status (default: watchlist)"
},
"addedAt": {
"type": "string",
"format": "date-time",
"description": "When the item was added to library"
}
},
"required": [
"title",
"type"
]
}
},
"ratings": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string"
},
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "Sofa 1-5 star rating"
},
"ratedAt": {
"type": "string",
"format": "date-time"
},
"ratedOn": {
"type": "string",
"format": "date"
}
},
"required": [
"title",
"type",
"rating"
]
}
}
},
"required": [
"source",
"movies",
"episodes",
"watchlist",
"ratings"
]
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
},
"stats": {
"type": "object",
"properties": {
"movies": {
"type": "number"
},
"episodes": {
"type": "number"
},
"watchlist": {
"type": "number"
},
"ratings": {
"type": "number"
}
},
"required": [
"movies",
"episodes",
"watchlist",
"ratings"
]
},
"diagnostics": {
"type": "object",
"properties": {
"unresolved": {
"type": "number"
},
"unsupported": {
"type": "number"
}
},
"required": [
"unresolved",
"unsupported"
]
},
"blockingErrors": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"data",
"warnings",
"stats"
]
}
}
}
},
"400": {
"description": "400",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "BAD_REQUEST"
},
"status": {
"const": 400
},
"message": {
"type": "string",
"default": "Invalid import file"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "IMPORT_INVALID_FILE"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/imports/parse-payload": {
"post": {
"operationId": "imports.parsePayload",
"summary": "Parse raw OAuth import data",
"description": "Accept raw API responses from the OAuth proxy, parse them into normalized import format, and return a preview with item counts.",
"tags": [
"Imports"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"source": {
"enum": [
"trakt",
"simkl"
],
"type": "string",
"description": "OAuth import provider"
},
"rawPayload": {
"description": "Raw aggregated API response from the OAuth proxy"
}
},
"required": [
"source"
]
}
}
}
},
"responses": {
"200": {
"description": "Preview of importable items with counts",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"movies": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"watchedAt": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp"
},
"watchedOn": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD date-only"
}
},
"required": [
"title"
]
}
},
"episodes": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"showTmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"showTitle": {
"type": "string"
},
"year": {
"type": "number"
},
"seasonNumber": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"episodeNumber": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"watchedAt": {
"type": "string",
"format": "date-time"
},
"watchedOn": {
"type": "string",
"format": "date"
}
},
"required": [
"seasonNumber",
"episodeNumber"
]
}
},
"watchlist": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string"
},
"status": {
"enum": [
"watchlist",
"in_progress",
"completed"
],
"type": "string",
"description": "Library status (default: watchlist)"
},
"addedAt": {
"type": "string",
"format": "date-time",
"description": "When the item was added to library"
}
},
"required": [
"title",
"type"
]
}
},
"ratings": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string"
},
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "Sofa 1-5 star rating"
},
"ratedAt": {
"type": "string",
"format": "date-time"
},
"ratedOn": {
"type": "string",
"format": "date"
}
},
"required": [
"title",
"type",
"rating"
]
}
}
},
"required": [
"source",
"movies",
"episodes",
"watchlist",
"ratings"
]
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
},
"stats": {
"type": "object",
"properties": {
"movies": {
"type": "number"
},
"episodes": {
"type": "number"
},
"watchlist": {
"type": "number"
},
"ratings": {
"type": "number"
}
},
"required": [
"movies",
"episodes",
"watchlist",
"ratings"
]
},
"diagnostics": {
"type": "object",
"properties": {
"unresolved": {
"type": "number"
},
"unsupported": {
"type": "number"
}
},
"required": [
"unresolved",
"unsupported"
]
},
"blockingErrors": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"data",
"warnings",
"stats"
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/imports/jobs": {
"post": {
"operationId": "imports.createJob",
"summary": "Create import job",
"description": "Create and start a background import job from previously parsed data.",
"tags": [
"Imports"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"movies": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"watchedAt": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp"
},
"watchedOn": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD date-only"
}
},
"required": [
"title"
]
}
},
"episodes": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"showTmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"showTitle": {
"type": "string"
},
"year": {
"type": "number"
},
"seasonNumber": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"episodeNumber": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"watchedAt": {
"type": "string",
"format": "date-time"
},
"watchedOn": {
"type": "string",
"format": "date"
}
},
"required": [
"seasonNumber",
"episodeNumber"
]
}
},
"watchlist": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string"
},
"status": {
"enum": [
"watchlist",
"in_progress",
"completed"
],
"type": "string",
"description": "Library status (default: watchlist)"
},
"addedAt": {
"type": "string",
"format": "date-time",
"description": "When the item was added to library"
}
},
"required": [
"title",
"type"
]
}
},
"ratings": {
"type": "array",
"maxItems": 50000,
"items": {
"type": "object",
"properties": {
"tmdbId": {
"type": "number"
},
"imdbId": {
"type": "string"
},
"tvdbId": {
"type": "number"
},
"title": {
"type": "string"
},
"year": {
"type": "number"
},
"type": {
"enum": [
"movie",
"tv"
],
"type": "string"
},
"rating": {
"type": "integer",
"minimum": 1,
"maximum": 5,
"description": "Sofa 1-5 star rating"
},
"ratedAt": {
"type": "string",
"format": "date-time"
},
"ratedOn": {
"type": "string",
"format": "date"
}
},
"required": [
"title",
"type",
"rating"
]
}
}
},
"required": [
"source",
"movies",
"episodes",
"watchlist",
"ratings"
]
},
"options": {
"type": "object",
"properties": {
"importWatches": {
"type": "boolean",
"description": "Import movie and episode watch history"
},
"importWatchlist": {
"type": "boolean",
"description": "Import watchlist items"
},
"importRatings": {
"type": "boolean",
"description": "Import ratings"
}
},
"required": [
"importWatches",
"importWatchlist",
"importRatings"
]
}
},
"required": [
"data",
"options"
]
}
}
}
},
"responses": {
"200": {
"description": "Created import job",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"status": {
"enum": [
"pending",
"running",
"success",
"error",
"cancelled"
],
"type": "string"
},
"totalItems": {
"type": "number"
},
"processedItems": {
"type": "number"
},
"importedCount": {
"type": "number"
},
"skippedCount": {
"type": "number"
},
"failedCount": {
"type": "number"
},
"currentMessage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"errors": {
"type": "array",
"items": {
"type": "string"
}
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
},
"createdAt": {
"type": "string"
},
"startedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"finishedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"source",
"status",
"totalItems",
"processedItems",
"importedCount",
"skippedCount",
"failedCount",
"currentMessage",
"errors",
"warnings",
"createdAt",
"startedAt",
"finishedAt"
]
}
}
}
},
"400": {
"description": "400",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "BAD_REQUEST"
},
"status": {
"const": 400
},
"message": {
"type": "string",
"default": "Import payload too large"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "IMPORT_PAYLOAD_TOO_LARGE"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
},
"409": {
"description": "409",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "CONFLICT"
},
"status": {
"const": 409
},
"message": {
"type": "string",
"default": "An import is already in progress"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "IMPORT_ALREADY_RUNNING"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/imports/jobs/{id}": {
"get": {
"operationId": "imports.getJob",
"summary": "Get import job status",
"description": "Get the current status and progress of an import job.",
"tags": [
"Imports"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Internal UUIDv7 identifier"
}
}
],
"responses": {
"200": {
"description": "Current job state",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"status": {
"enum": [
"pending",
"running",
"success",
"error",
"cancelled"
],
"type": "string"
},
"totalItems": {
"type": "number"
},
"processedItems": {
"type": "number"
},
"importedCount": {
"type": "number"
},
"skippedCount": {
"type": "number"
},
"failedCount": {
"type": "number"
},
"currentMessage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"errors": {
"type": "array",
"items": {
"type": "string"
}
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
},
"createdAt": {
"type": "string"
},
"startedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"finishedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"source",
"status",
"totalItems",
"processedItems",
"importedCount",
"skippedCount",
"failedCount",
"currentMessage",
"errors",
"warnings",
"createdAt",
"startedAt",
"finishedAt"
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/imports/jobs/{id}/cancel": {
"post": {
"operationId": "imports.cancelJob",
"summary": "Cancel import job",
"description": "Cancel a pending or running import job. Already-imported items are not rolled back.",
"tags": [
"Imports"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Internal UUIDv7 identifier"
}
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {
"200": {
"description": "Updated job state",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"status": {
"enum": [
"pending",
"running",
"success",
"error",
"cancelled"
],
"type": "string"
},
"totalItems": {
"type": "number"
},
"processedItems": {
"type": "number"
},
"importedCount": {
"type": "number"
},
"skippedCount": {
"type": "number"
},
"failedCount": {
"type": "number"
},
"currentMessage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"errors": {
"type": "array",
"items": {
"type": "string"
}
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
},
"createdAt": {
"type": "string"
},
"startedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"finishedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"source",
"status",
"totalItems",
"processedItems",
"importedCount",
"skippedCount",
"failedCount",
"currentMessage",
"errors",
"warnings",
"createdAt",
"startedAt",
"finishedAt"
]
}
}
}
},
"400": {
"description": "400",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"defined": {
"const": true
},
"code": {
"const": "BAD_REQUEST"
},
"status": {
"const": 400
},
"message": {
"type": "string",
"default": "Import cannot be cancelled"
},
"data": {
"type": "object",
"properties": {
"code": {
"const": "IMPORT_CANNOT_CANCEL"
}
},
"required": [
"code"
]
}
},
"required": [
"defined",
"code",
"status",
"message",
"data"
]
},
{
"type": "object",
"properties": {
"defined": {
"const": false
},
"code": {
"type": "string"
},
"status": {
"type": "number"
},
"message": {
"type": "string"
},
"data": {}
},
"required": [
"defined",
"code",
"status",
"message"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
},
"/imports/jobs/{id}/events": {
"get": {
"operationId": "imports.jobEvents",
"summary": "Stream import job events",
"description": "SSE stream of import job progress events. Yields progress updates and a final complete event.",
"tags": [
"Imports"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"minLength": 1,
"description": "Internal UUIDv7 identifier"
}
}
],
"responses": {
"200": {
"description": "Stream of job progress events",
"content": {
"text/event-stream": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"event": {
"const": "message"
},
"data": {
"type": "object",
"properties": {
"type": {
"enum": [
"progress",
"complete",
"timeout"
],
"type": "string"
},
"job": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"source": {
"enum": [
"trakt",
"simkl",
"letterboxd",
"sofa"
],
"type": "string",
"description": "Service to import from"
},
"status": {
"enum": [
"pending",
"running",
"success",
"error",
"cancelled"
],
"type": "string"
},
"totalItems": {
"type": "number"
},
"processedItems": {
"type": "number"
},
"importedCount": {
"type": "number"
},
"skippedCount": {
"type": "number"
},
"failedCount": {
"type": "number"
},
"currentMessage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"errors": {
"type": "array",
"items": {
"type": "string"
}
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
},
"createdAt": {
"type": "string"
},
"startedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"finishedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"source",
"status",
"totalItems",
"processedItems",
"importedCount",
"skippedCount",
"failedCount",
"currentMessage",
"errors",
"warnings",
"createdAt",
"startedAt",
"finishedAt"
]
}
},
"required": [
"type",
"job"
]
},
"id": {
"type": "string"
},
"retry": {
"type": "number"
}
},
"required": [
"event",
"data"
]
},
{
"type": "object",
"properties": {
"event": {
"const": "done"
},
"data": {},
"id": {
"type": "string"
},
"retry": {
"type": "number"
}
},
"required": [
"event"
]
},
{
"type": "object",
"properties": {
"event": {
"const": "error"
},
"data": {},
"id": {
"type": "string"
},
"retry": {
"type": "number"
}
},
"required": [
"event"
]
}
]
}
}
}
}
},
"security": [
{
"session": []
}
]
}
}
}
}