{
  "openapi": "3.0.3",
  "info": {
    "title": "Hollywood Metrics API",
    "description": "The definitive cinema analytics API. Access 20,000+ films, 1,500+ analyzed screenplays, ML-powered predictions, AI screenplay analysis, recommendation engine, poster intelligence, and craft profiles for writers, directors, cinematographers, and actors.\n\n**v2 is the recommended version** — canonical Film objects with GCS-resolved poster URLs, full cast and crew, cursor pagination, NDJSON streaming, and signed GCS catalog snapshots. v1 endpoints remain available for backwards compatibility.\n\nAll endpoints require API key authentication (Producer or Studio tier). Pass your key via the `X-API-Key` header or `api_key` query parameter. Rate limits reset daily at 00:00 UTC. Producer keys: 100 requests/day. Studio keys: 10,000 requests/day.",
    "version": "2.0.0",
    "contact": {
      "name": "Hollywood Metrics",
      "url": "https://hollywoodmetrics.ai",
      "email": "support@hollywoodmetrics.ai"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://hollywoodmetrics.ai/pricing"
    }
  },
  "servers": [
    {
      "url": "https://hollywoodmetrics.ai",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyHeader": []
    }
  ],
  "tags": [
    {
      "name": "Films",
      "description": "Search, filter, and retrieve film data from 20,000+ titles"
    },
    {
      "name": "Scripts",
      "description": "Analyzed screenplays with 20 quantitative features and ML tier predictions"
    },
    {
      "name": "Search",
      "description": "Unified search across films, scripts, synopses, cast, and crew"
    },
    {
      "name": "Predictions",
      "description": "Project outcome predictions and film recommendations"
    },
    {
      "name": "Styles",
      "description": "Craft profiles for screenwriters, directors, and cinematographers"
    },
    {
      "name": "Actors",
      "description": "Actor profiles with filmographies, career stats, and style guides"
    },
    {
      "name": "Posters",
      "description": "Poster intelligence and AI-powered poster analysis"
    },
    {
      "name": "AI",
      "description": "AI-powered screenplay analysis and rewrite (Studio tier required)"
    },
    {
      "name": "Model",
      "description": "ML model metadata and correlation data"
    },
    {
      "name": "Keys",
      "description": "API key management (admin only)"
    },
    {
      "name": "Discovery",
      "description": "API introspection and discovery"
    },
    {
      "name": "Films (v2)",
      "description": "Canonical Film objects: GCS poster URLs, full cast/crew, cursor pagination, NDJSON streaming"
    },
    {
      "name": "Recommendations (v2)",
      "description": "AI-assisted recommendations returning canonical Film objects"
    },
    {
      "name": "Catalog (v2)",
      "description": "Bulk catalog access via signed GCS snapshots"
    }
  ],
  "paths": {
    "/api/v1/films": {
      "get": {
        "operationId": "listFilms",
        "summary": "List and search films",
        "description": "Search and filter across 20,000+ films with pagination, sorting, and field selection. Results are enriched with synopses, keywords, cast, and crew from TMDB data when available.",
        "tags": [
          "Films"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search query. Matches against title, synopsis, keywords, cast, and crew.",
            "schema": {
              "type": "string"
            },
            "example": "inception"
          },
          {
            "name": "genre",
            "in": "query",
            "description": "Filter by genre (case-insensitive substring match).",
            "schema": {
              "type": "string"
            },
            "example": "thriller"
          },
          {
            "name": "decade",
            "in": "query",
            "description": "Filter by decade (e.g. '1990s' or '1990').",
            "schema": {
              "type": "string"
            },
            "example": "2010s"
          },
          {
            "name": "min_rating",
            "in": "query",
            "description": "Minimum IMDb rating.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 10
            },
            "example": 7.5
          },
          {
            "name": "min_score",
            "in": "query",
            "description": "Minimum master score.",
            "schema": {
              "type": "number",
              "minimum": 0
            },
            "example": 70
          },
          {
            "name": "director",
            "in": "query",
            "description": "Filter by director name (case-insensitive substring match).",
            "schema": {
              "type": "string"
            },
            "example": "nolan"
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort field and direction. Format: `field:direction`. Fields: title, year, imdb_rating, rt_score, tmdb_rating, master_score, budget, worldwide_gross, roi, oscar_noms, oscar_wins, runtime.",
            "schema": {
              "type": "string"
            },
            "example": "imdb_rating:desc"
          },
          {
            "name": "fields",
            "in": "query",
            "description": "Comma-separated list of fields to return. If omitted, all fields are returned.",
            "schema": {
              "type": "string"
            },
            "example": "title,imdb_rating,year"
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Pagination offset.",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size (1-200).",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of films.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Film"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginationMeta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "imdb_id": "tt1375666",
                      "title": "Inception",
                      "year": 2010,
                      "genres": "Action, Sci-Fi, Thriller",
                      "director": "Christopher Nolan",
                      "imdb_rating": 8.8,
                      "rt_score": 87,
                      "tmdb_rating": 8.4,
                      "master_score": 88.5,
                      "budget": 160000000,
                      "worldwide_gross": 836836967,
                      "roi": 5.23,
                      "oscar_noms": 8,
                      "oscar_wins": 4,
                      "language": "English",
                      "runtime": 148,
                      "overview": "A thief who steals corporate secrets through the use of dream-sharing technology...",
                      "tagline": "Your mind is the scene of the crime.",
                      "keywords": [
                        "dream",
                        "subconscious",
                        "heist"
                      ],
                      "cast": [
                        "Leonardo DiCaprio",
                        "Joseph Gordon-Levitt",
                        "Elliot Page"
                      ],
                      "crew": [
                        "Christopher Nolan (Director)",
                        "Hans Zimmer (Original Music Composer)"
                      ]
                    }
                  ],
                  "meta": {
                    "total": 20345,
                    "offset": 0,
                    "limit": 50
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/films/{id}": {
      "get": {
        "operationId": "getFilm",
        "summary": "Get a single film by IMDb ID",
        "tags": [
          "Films"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "IMDb ID of the film.",
            "schema": {
              "type": "string"
            },
            "example": "tt0111161"
          }
        ],
        "responses": {
          "200": {
            "description": "Single film data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Film"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/stats": {
      "get": {
        "operationId": "getStats",
        "summary": "Get aggregate database statistics",
        "description": "Returns summary statistics, genre breakdowns, decade trends, budget tiers, top directors, score distributions, rating distributions, year trends, language stats, Best Picture data, and ROI by decade.",
        "tags": [
          "Films"
        ],
        "responses": {
          "200": {
            "description": "Aggregate statistics object.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "summary": {
                          "type": "object",
                          "description": "High-level counts and averages."
                        },
                        "genres": {
                          "type": "object",
                          "description": "Genre breakdown with counts and average scores."
                        },
                        "decades": {
                          "type": "object",
                          "description": "Decade-level aggregations."
                        },
                        "budgetTiers": {
                          "type": "object",
                          "description": "Budget tier distributions."
                        },
                        "topDirectors": {
                          "type": "object",
                          "description": "Top directors by film count and score."
                        },
                        "scoreDist": {
                          "type": "object",
                          "description": "Master score distribution."
                        },
                        "ratingDist": {
                          "type": "object",
                          "description": "IMDb rating distribution."
                        },
                        "yearTrend": {
                          "type": "object",
                          "description": "Year-over-year trend data."
                        },
                        "languages": {
                          "type": "object",
                          "description": "Language distribution."
                        },
                        "bestPicture": {
                          "type": "object",
                          "description": "Best Picture nominees and winners."
                        },
                        "roiDecades": {
                          "type": "object",
                          "description": "ROI trends by decade."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/scripts": {
      "get": {
        "operationId": "listScripts",
        "summary": "List analyzed screenplays",
        "description": "Returns analyzed screenplays with 20 quantitative features, tier classification, and quality metrics. Filter by tier (S, A, B, C, D, E).",
        "tags": [
          "Scripts"
        ],
        "parameters": [
          {
            "name": "tier",
            "in": "query",
            "description": "Filter by screenplay tier (S, A, B, C, D, E).",
            "schema": {
              "type": "string",
              "enum": [
                "S",
                "A",
                "B",
                "C",
                "D",
                "E"
              ]
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of analyzed scripts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Script"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginationMeta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "title": "The Shawshank Redemption",
                      "year": 1994,
                      "tier": "S",
                      "rank": 1,
                      "points": 98,
                      "imdb_id": "tt0111161",
                      "imdb_rating": 9.3,
                      "master_score": 95.2,
                      "features": {
                        "scene_count": 142,
                        "dialogue_ratio": 0.38,
                        "action_ratio": 0.45,
                        "avg_sentence_len": 12.3,
                        "vocab_richness": 0.21
                      }
                    }
                  ],
                  "meta": {
                    "total": 1571,
                    "offset": 0,
                    "limit": 50
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/scripts/{id}": {
      "get": {
        "operationId": "getScript",
        "summary": "Get a single screenplay analysis by IMDb ID",
        "tags": [
          "Scripts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "IMDb ID of the screenplay.",
            "schema": {
              "type": "string"
            },
            "example": "tt0111161"
          }
        ],
        "responses": {
          "200": {
            "description": "Single script analysis with features.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Script"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/scripts/analyze": {
      "post": {
        "operationId": "analyzeScript",
        "summary": "Analyze screenplay text",
        "description": "Submit raw screenplay text for 20-feature extraction and ML-based tier prediction (S/A through D/E). Returns extracted features, predicted tier, class probabilities, and comparisons against dataset averages.",
        "tags": [
          "Scripts"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 500,
                    "description": "Raw screenplay text (minimum 500 characters)."
                  }
                }
              },
              "example": {
                "text": "FADE IN:\n\nINT. OFFICE - DAY\n\nA dimly lit office. JACK (40s, weathered) sits behind a desk covered in files..."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Analysis results with prediction and features.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "predicted_class": {
                          "type": "string",
                          "enum": [
                            "High",
                            "Mid",
                            "Low"
                          ],
                          "description": "ML classification result."
                        },
                        "predicted_tier": {
                          "type": "string",
                          "enum": [
                            "S/A",
                            "B/C",
                            "D/E"
                          ],
                          "description": "Mapped screenplay quality tier."
                        },
                        "probabilities": {
                          "type": "object",
                          "properties": {
                            "High": {
                              "type": "number"
                            },
                            "Mid": {
                              "type": "number"
                            },
                            "Low": {
                              "type": "number"
                            }
                          },
                          "description": "Class probabilities from Random Forest voting."
                        },
                        "features": {
                          "type": "object",
                          "description": "Extracted 20 quantitative features.",
                          "additionalProperties": {
                            "type": "number"
                          }
                        },
                        "comparisons": {
                          "type": "object",
                          "description": "Per-feature comparison against dataset averages (z-score, percentile).",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "number"
                              },
                              "dataset_mean": {
                                "type": "number"
                              },
                              "dataset_std": {
                                "type": "number"
                              },
                              "z_score": {
                                "type": "number"
                              },
                              "percentile_approx": {
                                "type": "number"
                              }
                            }
                          }
                        },
                        "script_length": {
                          "type": "integer",
                          "description": "Character count of submitted text."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "predicted_class": "High",
                    "predicted_tier": "S/A",
                    "probabilities": {
                      "High": 0.68,
                      "Mid": 0.24,
                      "Low": 0.08
                    },
                    "features": {
                      "scene_count": 98,
                      "dialogue_ratio": 0.42,
                      "action_ratio": 0.38,
                      "vocab_richness": 0.19
                    },
                    "comparisons": {
                      "scene_count": {
                        "value": 98,
                        "dataset_mean": 85.3,
                        "dataset_std": 32.1,
                        "z_score": 0.4,
                        "percentile_approx": 63.6
                      }
                    },
                    "script_length": 65432
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "operationId": "unifiedSearch",
        "summary": "Unified search across films, scripts, and synopses",
        "description": "Searches across titles, synopses/overviews, keywords, genres, cast, and crew. Results are scored by relevance and include match context.",
        "tags": [
          "Search"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search query (minimum 2 characters).",
            "schema": {
              "type": "string",
              "minLength": 2
            },
            "example": "time travel"
          },
          {
            "name": "type",
            "in": "query",
            "description": "Restrict search to a specific type.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "films",
                "scripts",
                "synopsis"
              ],
              "default": "all"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum results (1-100).",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked search results with match context.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SearchResult"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "returned": {
                          "type": "integer"
                        },
                        "query": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "type": "film",
                      "title": "Back to the Future",
                      "year": "1985",
                      "match_field": "keywords",
                      "match_snippet": "time travel, time machine",
                      "score": 14,
                      "data": {
                        "imdb_id": "tt0088763",
                        "tmdb_id": 105,
                        "genres": [
                          "Adventure",
                          "Comedy",
                          "Science Fiction"
                        ],
                        "overview": "Eighties teenager Marty McFly is accidentally sent back in time...",
                        "vote_average": 8.3
                      }
                    }
                  ],
                  "meta": {
                    "total": 47,
                    "returned": 20,
                    "query": "time travel",
                    "type": "all"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/predict": {
      "post": {
        "operationId": "predictOutcome",
        "summary": "Predict project box office outcome",
        "description": "Combines comparable film analysis, audience segmentation, and marketing intelligence to predict a project's commercial potential. Returns estimated worldwide gross, ratings, ROI, audience profile, and marketing recommendations.",
        "tags": [
          "Predictions"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "description": "Project title."
                  },
                  "genre": {
                    "type": "string",
                    "description": "Comma-separated genres (alternative to genres array)."
                  },
                  "genres": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Array of genres."
                  },
                  "budget": {
                    "type": "number",
                    "description": "Production budget in USD."
                  },
                  "director": {
                    "type": "string",
                    "description": "Director name."
                  },
                  "cast": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Array of cast member names."
                  },
                  "synopsis": {
                    "type": "string",
                    "description": "Project synopsis for thematic matching."
                  },
                  "screenplay_text": {
                    "type": "string",
                    "description": "Optional screenplay text for deeper analysis."
                  }
                }
              },
              "example": {
                "title": "The Last Algorithm",
                "genres": [
                  "Sci-Fi",
                  "Thriller"
                ],
                "budget": 45000000,
                "director": "Denis Villeneuve",
                "cast": [
                  "Timothee Chalamet",
                  "Zendaya"
                ],
                "synopsis": "In a near-future society controlled by AI, a rogue programmer discovers the source code that could free humanity."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Prediction results with comparables and marketing insights.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "project": {
                          "type": "object",
                          "properties": {
                            "title": {
                              "type": "string"
                            },
                            "genres": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "budget": {
                              "type": "number"
                            },
                            "director": {
                              "type": "string"
                            },
                            "cast": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "prediction": {
                          "type": "object",
                          "properties": {
                            "estimated_worldwide_gross": {
                              "type": "integer",
                              "nullable": true
                            },
                            "estimated_imdb_rating": {
                              "type": "number",
                              "nullable": true
                            },
                            "estimated_master_score": {
                              "type": "number",
                              "nullable": true
                            },
                            "estimated_roi": {
                              "type": "number",
                              "nullable": true
                            },
                            "confidence": {
                              "type": "string",
                              "enum": [
                                "low",
                                "medium",
                                "high"
                              ]
                            },
                            "comparable_count": {
                              "type": "integer"
                            }
                          }
                        },
                        "audience": {
                          "type": "object",
                          "description": "Audience segmentation and release strategy.",
                          "properties": {
                            "primary_demographic": {
                              "type": "string"
                            },
                            "release_strategy": {
                              "type": "string"
                            },
                            "marketing_budget_estimate": {
                              "type": "string"
                            },
                            "audience_reception_forecast": {
                              "type": "string"
                            }
                          }
                        },
                        "marketing": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Actionable marketing recommendations."
                        },
                        "comparables": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ComparableFilm"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/recommend": {
      "post": {
        "operationId": "getRecommendations",
        "summary": "Get film recommendations",
        "description": "Returns personalized film recommendations based on liked/disliked films. Uses genre, keyword, director, cast, and quality signals to build a taste profile and score candidates. Accepts IMDb IDs or film titles.",
        "tags": [
          "Predictions"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "liked"
                ],
                "properties": {
                  "liked": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "description": "Films the user likes (IMDb IDs or titles)."
                  },
                  "disliked": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Films the user dislikes (IMDb IDs or titles)."
                  },
                  "limit": {
                    "type": "integer",
                    "default": 20,
                    "maximum": 100,
                    "description": "Max recommendations to return."
                  }
                }
              },
              "example": {
                "liked": [
                  "tt1375666",
                  "Blade Runner 2049",
                  "tt0133093"
                ],
                "disliked": [
                  "tt0120338"
                ],
                "limit": 10
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked recommendations with taste profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "recommendations": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "imdb_id": {
                                "type": "string"
                              },
                              "title": {
                                "type": "string"
                              },
                              "year": {
                                "type": "string",
                                "nullable": true
                              },
                              "genres": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "vote_average": {
                                "type": "number",
                                "nullable": true
                              },
                              "overview": {
                                "type": "string"
                              },
                              "poster_path": {
                                "type": "string",
                                "nullable": true
                              },
                              "score": {
                                "type": "number"
                              },
                              "reasons": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        },
                        "taste_profile": {
                          "type": "object",
                          "properties": {
                            "top_genres": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "top_keywords": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "liked_directors": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "liked_actors": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "input": {
                          "type": "object",
                          "properties": {
                            "liked_resolved": {
                              "type": "integer"
                            },
                            "disliked_resolved": {
                              "type": "integer"
                            },
                            "total_scored": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/styles/writers": {
      "get": {
        "operationId": "listWriterStyles",
        "summary": "List screenwriter style guides",
        "description": "Returns 88+ screenwriter style guides with techniques, specifications, and writing patterns.",
        "tags": [
          "Styles"
        ],
        "responses": {
          "200": {
            "description": "Screenwriter style guide data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Style tips data object with a `styles` key containing all writer guides."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/styles/directors": {
      "get": {
        "operationId": "listDirectorStyles",
        "summary": "List director craft profiles",
        "description": "Returns 1,600+ director craft profiles with TMDB data, career stats, filmographies, and visual philosophy.",
        "tags": [
          "Styles"
        ],
        "responses": {
          "200": {
            "description": "Director profiles array.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "filename": {
                            "type": "string"
                          },
                          "content": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/styles/cinematographers": {
      "get": {
        "operationId": "listCinematographerStyles",
        "summary": "List cinematographer profiles",
        "description": "Returns 475+ cinematographer profiles with visual philosophy, career stats, and technique breakdowns.",
        "tags": [
          "Styles"
        ],
        "responses": {
          "200": {
            "description": "Cinematographer profiles array.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "filename": {
                            "type": "string"
                          },
                          "content": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/actors": {
      "get": {
        "operationId": "listActors",
        "summary": "List and search actor profiles",
        "description": "Returns 500+ actor profiles with headshots, visual descriptions, biographies, filmographies, career stats, and style guide data. Sorted by TMDB popularity.",
        "tags": [
          "Actors"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search by actor name (case-insensitive).",
            "schema": {
              "type": "string"
            },
            "example": "cate blanchett"
          },
          {
            "name": "genre",
            "in": "query",
            "description": "Filter by top genre in career stats.",
            "schema": {
              "type": "string"
            },
            "example": "drama"
          },
          {
            "name": "has_style",
            "in": "query",
            "description": "Set to 'true' to only return actors with style guide entries.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "min_popularity",
            "in": "query",
            "description": "Minimum TMDB popularity score.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of actor profiles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Actor"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginationMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/actors/{id}": {
      "get": {
        "operationId": "getActor",
        "summary": "Get a single actor profile",
        "description": "Retrieve a full actor profile by TMDB ID, IMDb ID (nm-prefixed), or name slug (e.g. 'cate-blanchett'). Includes full filmography, style guide techniques, and specifications.",
        "tags": [
          "Actors"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "TMDB ID, IMDb ID (e.g. nm0000949), or name slug (e.g. cate-blanchett).",
            "schema": {
              "type": "string"
            },
            "example": "cate-blanchett"
          }
        ],
        "responses": {
          "200": {
            "description": "Full actor profile with merged TMDB and style guide data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ActorDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/correlations": {
      "get": {
        "operationId": "getCorrelations",
        "summary": "Get screenplay feature correlations",
        "description": "Returns the full correlation dataset: 20 screenplay features correlated against 8 outcome metrics (Pearson r heatmap), per-tier profiles, Cohen's d effect sizes (S-tier vs E-tier), and auto-generated insights.",
        "tags": [
          "Model"
        ],
        "responses": {
          "200": {
            "description": "Full correlation data object.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Correlation matrices, tier profiles, effect sizes, and insights."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/model": {
      "get": {
        "operationId": "getModelMetadata",
        "summary": "Get ML model metadata",
        "description": "Returns model type, classes, feature names, feature importances, feature categories, feature statistics (mean/std), logistic regression coefficients, and total scripts analyzed. Does not include tree data.",
        "tags": [
          "Model"
        ],
        "responses": {
          "200": {
            "description": "Model metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "model_type": {
                          "type": "string",
                          "example": "RandomForestClassifier"
                        },
                        "classes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "example": [
                            "High",
                            "Low",
                            "Mid"
                          ]
                        },
                        "n_trees": {
                          "type": "integer",
                          "example": 100
                        },
                        "macro_f1": {
                          "type": "number",
                          "example": 0.72
                        },
                        "feature_names": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "example": [
                            "scene_count",
                            "dialogue_ratio",
                            "action_ratio"
                          ]
                        },
                        "feature_labels": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "feature_categories": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "feature_stats": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "mean": {
                                "type": "number"
                              },
                              "std": {
                                "type": "number"
                              }
                            }
                          }
                        },
                        "importances": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "number"
                          }
                        },
                        "lr_coefficients": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "object"
                          }
                        },
                        "total_scripts": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posters/stats": {
      "get": {
        "operationId": "getPosterStats",
        "summary": "Get poster intelligence aggregates",
        "description": "Returns aggregated poster analysis statistics including poster DNA stats, deep poster analysis summaries, batch analysis summaries, coverage data, and sentiment stats.",
        "tags": [
          "Posters"
        ],
        "responses": {
          "200": {
            "description": "Poster intelligence aggregates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "poster_dna": {
                          "type": "object"
                        },
                        "deep_poster": {
                          "type": "object"
                        },
                        "batch_poster": {
                          "type": "object"
                        },
                        "coverage": {
                          "type": "object"
                        },
                        "sentiment": {
                          "type": "object"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posters/films": {
      "get": {
        "operationId": "getPosterFilms",
        "summary": "Get per-film poster analysis",
        "description": "Returns per-film poster analysis data with pagination. Optionally filter by a specific film's IMDb ID.",
        "tags": [
          "Posters"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "Filter by IMDb ID.",
            "schema": {
              "type": "string"
            },
            "example": "tt1375666"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated poster analysis results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/PaginationMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/posters/analyze": {
      "post": {
        "operationId": "analyzePoster",
        "summary": "AI poster analysis",
        "description": "Upload a poster image (base64-encoded) for AI-powered visual analysis. Returns composition, genre signals, marketing effectiveness, design quality score, comparable posters, and improvement suggestions. Requires Studio tier.",
        "tags": [
          "Posters"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "image_b64"
                ],
                "properties": {
                  "image_b64": {
                    "type": "string",
                    "description": "Base64-encoded poster image data."
                  },
                  "mime_type": {
                    "type": "string",
                    "default": "image/jpeg",
                    "description": "MIME type of the image.",
                    "enum": [
                      "image/jpeg",
                      "image/png",
                      "image/webp"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI poster analysis results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "analysis": {
                          "type": "string",
                          "description": "Full AI analysis text."
                        },
                        "tokens": {
                          "type": "object",
                          "properties": {
                            "input": {
                              "type": "integer"
                            },
                            "output": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Studio tier required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ai/analyze-script": {
      "post": {
        "operationId": "aiAnalyzeScript",
        "summary": "AI-powered deep screenplay analysis",
        "description": "Submit screenplay text for comprehensive AI analysis covering structure, dialogue, character, tone, commercial viability, craft strengths, and revision priorities. Uses Claude for analysis. Requires Studio tier.",
        "tags": [
          "AI"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 500,
                    "description": "Raw screenplay text (minimum 500 characters, max ~80,000)."
                  },
                  "focus": {
                    "type": "string",
                    "enum": [
                      "structure",
                      "dialogue",
                      "all"
                    ],
                    "default": "all",
                    "description": "Focus area for the analysis."
                  }
                }
              },
              "example": {
                "text": "FADE IN:\n\nEXT. CITY SKYLINE - DAWN\n\nThe sun rises over a sprawling metropolis...",
                "focus": "structure"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI analysis results with extracted features.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "analysis": {
                          "type": "string",
                          "description": "Full AI analysis text."
                        },
                        "features": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "number"
                          }
                        },
                        "focus": {
                          "type": "string"
                        },
                        "script_length": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Studio tier required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ai/rewrite": {
      "post": {
        "operationId": "aiRewriteScript",
        "summary": "AI screenplay rewrite in a screenwriter's style",
        "description": "Submit screenplay text and a style guide name to get an AI-powered rewrite in that screenwriter's distinctive voice. Maintains core story while transforming dialogue rhythm, scene description, and structural choices. Requires Studio tier.",
        "tags": [
          "AI"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text",
                  "style"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 100,
                    "description": "Raw screenplay text (minimum 100 characters, max ~60,000)."
                  },
                  "style": {
                    "type": "string",
                    "description": "Screenwriter style slug (e.g. 'aaron-sorkin', 'quentin-tarantino'). Use GET /api/v1/styles/writers to list available styles."
                  }
                }
              },
              "example": {
                "text": "INT. COFFEE SHOP - DAY\n\nJANE sits alone at a corner table, nursing a cold latte...",
                "style": "aaron-sorkin"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI rewrite results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "rewrite": {
                          "type": "string",
                          "description": "Full rewritten screenplay text."
                        },
                        "style": {
                          "type": "string",
                          "description": "Style that was applied."
                        },
                        "original_length": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Studio tier required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Style guide not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/keys/generate": {
      "post": {
        "operationId": "generateApiKey",
        "summary": "Generate a new API key",
        "description": "Generate a new V1 API key for a user. Requires superuser authentication via Firebase auth token in the Authorization header. If the user already has an active key, returns the existing key.",
        "tags": [
          "Keys"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "uid",
                  "email"
                ],
                "properties": {
                  "uid": {
                    "type": "string",
                    "description": "Firebase user UID."
                  },
                  "email": {
                    "type": "string",
                    "description": "User email."
                  },
                  "tier": {
                    "type": "string",
                    "enum": [
                      "producer",
                      "studio"
                    ],
                    "default": "studio",
                    "description": "API key tier."
                  },
                  "name": {
                    "type": "string",
                    "default": "Default Key",
                    "description": "Display name for the key."
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "read",
                        "write",
                        "ai",
                        "admin"
                      ]
                    },
                    "default": [
                      "read",
                      "write",
                      "ai",
                      "admin"
                    ],
                    "description": "Permission scopes for the key."
                  }
                }
              },
              "example": {
                "uid": "abc123",
                "email": "user@example.com",
                "tier": "producer",
                "name": "Production Key",
                "scopes": [
                  "read",
                  "write"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key already exists for this user.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "api_key": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "New API key generated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "api_key": {
                          "type": "string",
                          "example": "hm_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
                        },
                        "email": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "description": "Admin authentication required."
          }
        }
      }
    },
    "/api/v1/keys/list": {
      "get": {
        "operationId": "listApiKeys",
        "summary": "List all API keys with usage stats",
        "description": "Returns all API keys with usage metrics including total requests, today's requests, last used timestamp, and scopes. Requires superuser authentication.",
        "tags": [
          "Keys"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of API keys with stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key_preview": {
                            "type": "string",
                            "example": "hm_a1b2c3..."
                          },
                          "full_key": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "uid": {
                            "type": "string"
                          },
                          "tier": {
                            "type": "string",
                            "enum": [
                              "producer",
                              "studio"
                            ]
                          },
                          "created": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "revoked": {
                            "type": "boolean"
                          },
                          "revokedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "active": {
                            "type": "boolean"
                          },
                          "daily_limit": {
                            "type": "integer"
                          },
                          "totalRequests": {
                            "type": "integer"
                          },
                          "todayRequests": {
                            "type": "integer"
                          },
                          "lastUsed": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "lastEndpoint": {
                            "type": "string",
                            "nullable": true
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Admin authentication required."
          }
        }
      }
    },
    "/api/v1/keys/revoke": {
      "post": {
        "operationId": "revokeApiKey",
        "summary": "Revoke an API key",
        "description": "Permanently revoke an API key. The key will immediately stop working. Requires superuser authentication.",
        "tags": [
          "Keys"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "api_key"
                ],
                "properties": {
                  "api_key": {
                    "type": "string",
                    "description": "The full API key to revoke."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key revoked successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "Key revoked"
                        },
                        "api_key": {
                          "type": "string",
                          "example": "hm_a1b2c3..."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Admin authentication required."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/keys/rename": {
      "put": {
        "operationId": "renameApiKey",
        "summary": "Rename an API key",
        "description": "Update the display name of an API key. Requires superuser authentication.",
        "tags": [
          "Keys"
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "api_key",
                  "name"
                ],
                "properties": {
                  "api_key": {
                    "type": "string",
                    "description": "The full API key to rename."
                  },
                  "name": {
                    "type": "string",
                    "description": "New display name for the key."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key renamed successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string",
                          "example": "Key renamed"
                        },
                        "name": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "description": "Admin authentication required."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/endpoints": {
      "get": {
        "operationId": "listEndpoints",
        "summary": "List all available API endpoints",
        "description": "Returns a complete list of all API endpoints with descriptions, methods, and required tiers. No authentication required.",
        "tags": [
          "Discovery"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Endpoint directory.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "method": {
                            "type": "string",
                            "enum": [
                              "GET",
                              "POST",
                              "PUT",
                              "DELETE"
                            ]
                          },
                          "path": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "tier": {
                            "type": "string",
                            "enum": [
                              "public",
                              "writer",
                              "producer",
                              "studio"
                            ]
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "version": {
                          "type": "string"
                        },
                        "total_endpoints": {
                          "type": "integer"
                        },
                        "base_url": {
                          "type": "string"
                        },
                        "rate_limits": {
                          "type": "object"
                        },
                        "tiers": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "authentication": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/admin/usage": {
      "get": {
        "operationId": "getApiUsage",
        "summary": "Get API usage analytics",
        "description": "Returns API usage statistics including total requests, per-key breakdowns, daily trends (30 days), and top endpoints.",
        "tags": [
          "Keys"
        ],
        "responses": {
          "200": {
            "description": "Usage analytics.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total_requests": {
                          "type": "integer"
                        },
                        "total_keys": {
                          "type": "integer"
                        },
                        "active_keys": {
                          "type": "integer"
                        },
                        "requests_today": {
                          "type": "integer"
                        },
                        "daily_trend": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": "string",
                                "format": "date"
                              },
                              "requests": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "top_endpoints": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "endpoint": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "per_key": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "key_preview": {
                                "type": "string"
                              },
                              "email": {
                                "type": "string"
                              },
                              "tier": {
                                "type": "string"
                              },
                              "total": {
                                "type": "integer"
                              },
                              "today": {
                                "type": "integer"
                              },
                              "last_7d": {
                                "type": "integer"
                              },
                              "last_30d": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/films": {
      "get": {
        "operationId": "v2ListFilms",
        "summary": "Search and browse the catalog (canonical Film objects)",
        "description": "Returns canonical Film objects in list mode (top 8 cast, key crew, poster URLs). Supports cursor pagination via the `cursor` parameter — the response's `meta.next_cursor` is the token for the next page.",
        "tags": [
          "Films (v2)"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search across title, overview, keywords, cast, and crew.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "genre",
            "in": "query",
            "description": "One or more genres, comma-separated. Multiple genres are ANDed (e.g. `?genre=action,comedy` returns films tagged both Action AND Comedy).",
            "schema": {
              "type": "string"
            },
            "examples": {
              "single": {
                "value": "Sci-Fi"
              },
              "multi": {
                "value": "action,comedy"
              }
            }
          },
          {
            "name": "decade",
            "in": "query",
            "description": "e.g. `1990s` or `1990`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "year_min",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "year_max",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "min_rating",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 10
            }
          },
          {
            "name": "min_score",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 100
            }
          },
          {
            "name": "min_votes",
            "in": "query",
            "description": "Minimum IMDb votes. Use to filter out obscure films from sort-by-rating queries.",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "example": 10000
          },
          {
            "name": "director",
            "in": "query",
            "description": "Substring match against the director name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "actor",
            "in": "query",
            "description": "Substring match against any cast member's name (uses rec_data cast lookup; films without rec_data cast are excluded).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language",
            "in": "query",
            "description": "ISO 639-1 code (e.g. `en`, `ja`). Exact match.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language_not",
            "in": "query",
            "description": "Comma-separated ISO 639-1 codes to EXCLUDE. e.g. `language_not=en` returns non-English films; `en,fr` excludes English and French.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_runtime",
            "in": "query",
            "description": "Minimum runtime in minutes.",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "example": 180
          },
          {
            "name": "max_runtime",
            "in": "query",
            "description": "Maximum runtime in minutes.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "min_gross",
            "in": "query",
            "description": "Minimum worldwide gross in USD.",
            "schema": {
              "type": "number",
              "minimum": 0
            },
            "example": 1000000000
          },
          {
            "name": "max_gross",
            "in": "query",
            "description": "Maximum worldwide gross in USD.",
            "schema": {
              "type": "number",
              "minimum": 0
            }
          },
          {
            "name": "has_poster",
            "in": "query",
            "description": "If `true`, only return films with a resolved poster URL.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "`field:asc|desc`. Fields: title, year, imdb_rating, master_score, roi, worldwide_gross, runtime, oscar_wins.",
            "schema": {
              "type": "string"
            },
            "example": "imdb_rating:desc"
          },
          {
            "name": "fields",
            "in": "query",
            "description": "Comma-separated field projection.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque pagination token from the previous response's `meta.next_cursor`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of films",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "example": "public, max-age=300, stale-while-revalidate=3600"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CanonicalFilm"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/V2PaginationMeta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v2/films/{id}": {
      "get": {
        "operationId": "v2GetFilm",
        "summary": "Full canonical Film by IMDb ID",
        "description": "Detail mode: returns all cast, full crew grouped by department, all production companies, watch providers.",
        "tags": [
          "Films (v2)"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "tt1375666"
          }
        ],
        "responses": {
          "200": {
            "description": "Canonical Film",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CanonicalFilm"
                    },
                    "meta": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v2/films/{id}/similar": {
      "get": {
        "operationId": "v2GetSimilarFilms",
        "summary": "TMDB-driven similar films",
        "description": "Returns up to `limit` films from the TMDB similarity graph, each as a canonical Film object.",
        "tags": [
          "Films (v2)"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of similar films",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CanonicalFilm"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v2/films/{id}/poster-analysis": {
      "get": {
        "operationId": "v2GetPosterAnalysis",
        "summary": "AI-derived poster metadata",
        "description": "Returns mood, palette, composition, typography, era, and key visual elements from the poster_ai dataset. Available when `poster.ai_analysis_available` is `true` on the parent Film.",
        "tags": [
          "Films (v2)"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Poster analysis",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PosterAnalysis"
                    },
                    "meta": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v2/films/stream": {
      "get": {
        "operationId": "v2StreamFilms",
        "summary": "NDJSON stream of the full catalog",
        "description": "Streams one canonical Film per line as `application/x-ndjson`. The first line is a header with metadata; the last line is a footer with the final count. Filter parameters mirror `/api/v2/films`. Counts as a single request against your rate limit.",
        "tags": [
          "Films (v2)"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search across title, overview, keywords, cast, and crew.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "genre",
            "in": "query",
            "description": "One or more genres, comma-separated. Multiple genres are ANDed (e.g. `?genre=action,comedy` returns films tagged both Action AND Comedy).",
            "schema": {
              "type": "string"
            },
            "examples": {
              "single": {
                "value": "Sci-Fi"
              },
              "multi": {
                "value": "action,comedy"
              }
            }
          },
          {
            "name": "decade",
            "in": "query",
            "description": "e.g. `1990s` or `1990`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "year_min",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "year_max",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "min_rating",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 10
            }
          },
          {
            "name": "min_score",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 100
            }
          },
          {
            "name": "min_votes",
            "in": "query",
            "description": "Minimum IMDb votes. Use to filter out obscure films from sort-by-rating queries.",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "example": 10000
          },
          {
            "name": "director",
            "in": "query",
            "description": "Substring match against the director name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "actor",
            "in": "query",
            "description": "Substring match against any cast member's name (uses rec_data cast lookup; films without rec_data cast are excluded).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language",
            "in": "query",
            "description": "ISO 639-1 code (e.g. `en`, `ja`). Exact match.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language_not",
            "in": "query",
            "description": "Comma-separated ISO 639-1 codes to EXCLUDE. e.g. `language_not=en` returns non-English films; `en,fr` excludes English and French.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_runtime",
            "in": "query",
            "description": "Minimum runtime in minutes.",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "example": 180
          },
          {
            "name": "max_runtime",
            "in": "query",
            "description": "Maximum runtime in minutes.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "min_gross",
            "in": "query",
            "description": "Minimum worldwide gross in USD.",
            "schema": {
              "type": "number",
              "minimum": 0
            },
            "example": 1000000000
          },
          {
            "name": "max_gross",
            "in": "query",
            "description": "Maximum worldwide gross in USD.",
            "schema": {
              "type": "number",
              "minimum": 0
            }
          },
          {
            "name": "has_poster",
            "in": "query",
            "description": "If `true`, only return films with a resolved poster URL.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "list",
                "detail"
              ],
              "default": "list"
            }
          },
          {
            "name": "chunk_size",
            "in": "query",
            "description": "Films per network flush. Default 100, max 1000.",
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "NDJSON stream",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string",
                  "example": "{\"header\":{\"version\":\"v2\",\"mode\":\"list\",\"generated_at\":\"2026-05-12T00:00:00Z\",\"source_total\":20000}}\n{\"imdb_id\":\"tt0060196\",\"title\":\"The Good, the Bad and the Ugly\",...}\n{\"footer\":{\"written\":20000,\"complete\":true}}\n"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/catalog/snapshot": {
      "get": {
        "operationId": "v2GetCatalogSnapshot",
        "summary": "Stream the gzipped NDJSON catalog snapshot",
        "description": "Streams the full canonical catalog as gzipped NDJSON directly through this endpoint (no public URL, no redirect). Counts as one request against your rate limit regardless of size. ~18 MB compressed, ~74 MB uncompressed, 20,000 films.\n\nPass `?format=json` to receive only the manifest (sha256, film_count, sizes, generated_at) without the file body. Poll the manifest cheaply to detect new snapshots without re-downloading.",
        "tags": [
          "Catalog (v2)"
        ],
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "stream",
                "json"
              ],
              "default": "stream"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Snapshot stream or manifest (depending on `format`)",
            "headers": {
              "Content-Type": {
                "schema": {
                  "type": "string",
                  "example": "application/x-ndjson"
                }
              },
              "Content-Encoding": {
                "schema": {
                  "type": "string",
                  "example": "gzip"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "example": "private, max-age=300"
                }
              },
              "X-Catalog-Version": {
                "schema": {
                  "type": "string",
                  "example": "v2"
                }
              },
              "X-Catalog-Generated-At": {
                "schema": {
                  "type": "string",
                  "example": "2026-05-13T06:54:51.213Z"
                }
              },
              "X-Catalog-Film-Count": {
                "schema": {
                  "type": "string",
                  "example": "20000"
                }
              },
              "X-Catalog-Sha256": {
                "schema": {
                  "type": "string",
                  "example": "41608bb0..."
                }
              }
            },
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string",
                  "description": "gzipped NDJSON stream; identical line shape to /api/v2/films/stream"
                }
              },
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "manifest": {
                          "type": "object",
                          "additionalProperties": true
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Snapshot not currently available"
          }
        }
      }
    },
    "/api/v2/recommend": {
      "post": {
        "operationId": "v2Recommend",
        "summary": "AI-assisted recommendations (canonical Film objects)",
        "description": "Returns canonical Film objects ranked by a taste profile derived from `liked` and `disliked` IMDb IDs or titles. Each result includes a `recommendation.score` (0-100ish) and up to 3 `recommendation.reasons` (e.g. `Director: Christopher Nolan`).",
        "tags": [
          "Recommendations (v2)"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecommendV2Request"
              },
              "example": {
                "liked": [
                  "tt1375666",
                  "The Matrix"
                ],
                "disliked": [
                  "tt0468569"
                ],
                "limit": 20
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked recommendations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/CanonicalFilm"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "recommendation": {
                                "type": "object",
                                "properties": {
                                  "score": {
                                    "type": "number"
                                  },
                                  "reasons": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        ]
                      }
                    },
                    "meta": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body or no liked films resolved"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key for V1 endpoints. Generate via the admin panel or POST /api/v1/keys/generate. Can also be passed as `api_key` query parameter."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Firebase ID Token",
        "description": "Firebase Auth ID token. Required for admin/key management endpoints."
      }
    },
    "schemas": {
      "Film": {
        "type": "object",
        "properties": {
          "imdb_id": {
            "type": "string",
            "example": "tt1375666"
          },
          "title": {
            "type": "string",
            "example": "Inception"
          },
          "year": {
            "type": "integer",
            "example": 2010
          },
          "genres": {
            "type": "string",
            "example": "Action, Sci-Fi, Thriller"
          },
          "director": {
            "type": "string",
            "example": "Christopher Nolan"
          },
          "imdb_rating": {
            "type": "number",
            "example": 8.8
          },
          "rt_score": {
            "type": "integer",
            "example": 87
          },
          "tmdb_rating": {
            "type": "number",
            "example": 8.4
          },
          "master_score": {
            "type": "number",
            "example": 88.5
          },
          "budget": {
            "type": "integer",
            "example": 160000000
          },
          "worldwide_gross": {
            "type": "integer",
            "example": 836836967
          },
          "roi": {
            "type": "number",
            "example": 5.23
          },
          "oscar_noms": {
            "type": "integer",
            "example": 8
          },
          "oscar_wins": {
            "type": "integer",
            "example": 4
          },
          "language": {
            "type": "string",
            "example": "English"
          },
          "runtime": {
            "type": "integer",
            "example": 148
          },
          "overview": {
            "type": "string",
            "nullable": true,
            "description": "Synopsis from TMDB (when available)."
          },
          "tagline": {
            "type": "string",
            "nullable": true
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "TMDB keywords (when available)."
          },
          "cast": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Top 5 cast members (when available)."
          },
          "crew": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Top 5 crew members with role (when available)."
          }
        }
      },
      "Script": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "example": "The Shawshank Redemption"
          },
          "year": {
            "type": "integer",
            "example": 1994
          },
          "tier": {
            "type": "string",
            "example": "S"
          },
          "rank": {
            "type": "integer",
            "example": 1
          },
          "points": {
            "type": "integer",
            "example": 98
          },
          "imdb_id": {
            "type": "string",
            "example": "tt0111161"
          },
          "imdb_rating": {
            "type": "number",
            "example": 9.3
          },
          "master_score": {
            "type": "number",
            "example": 95.2
          },
          "features": {
            "type": "object",
            "description": "20 quantitative screenplay features keyed by name.",
            "additionalProperties": {
              "type": "number"
            }
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "film",
              "script"
            ],
            "description": "Result type."
          },
          "title": {
            "type": "string"
          },
          "year": {
            "type": "string",
            "nullable": true
          },
          "match_field": {
            "type": "string",
            "description": "Field that matched (title, synopsis, keywords, genre, cast, crew)."
          },
          "match_snippet": {
            "type": "string",
            "description": "Context snippet around the match."
          },
          "score": {
            "type": "number",
            "description": "Relevance score."
          },
          "data": {
            "type": "object",
            "description": "Full record data."
          }
        }
      },
      "ComparableFilm": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "year": {
            "type": "string",
            "nullable": true
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "budget": {
            "type": "integer",
            "nullable": true
          },
          "revenue": {
            "type": "integer",
            "nullable": true
          },
          "vote_average": {
            "type": "number",
            "nullable": true
          },
          "imdb_rating": {
            "type": "number",
            "nullable": true
          },
          "master_score": {
            "type": "number",
            "nullable": true
          },
          "similarity_score": {
            "type": "number"
          },
          "similarity_reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Actor": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "tmdb_id": {
            "type": "integer"
          },
          "imdb_id": {
            "type": "string",
            "nullable": true
          },
          "biography": {
            "type": "string"
          },
          "visual_description": {
            "type": "string"
          },
          "headshot": {
            "type": "string",
            "nullable": true
          },
          "headshot_url": {
            "type": "string",
            "nullable": true
          },
          "profile_path": {
            "type": "string",
            "nullable": true
          },
          "birthday": {
            "type": "string",
            "nullable": true
          },
          "deathday": {
            "type": "string",
            "nullable": true
          },
          "place_of_birth": {
            "type": "string",
            "nullable": true
          },
          "gender": {
            "type": "integer"
          },
          "popularity": {
            "type": "number"
          },
          "top_films": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "year": {
                  "type": "string"
                },
                "character": {
                  "type": "string"
                },
                "rating": {
                  "type": "number"
                },
                "poster_url": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "career_stats": {
            "type": "object",
            "nullable": true,
            "properties": {
              "total_films": {
                "type": "integer"
              },
              "avg_rating": {
                "type": "number"
              },
              "top_genres": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "genre": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              },
              "decades": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                }
              },
              "career_start": {
                "type": "integer"
              },
              "career_latest": {
                "type": "integer"
              }
            }
          },
          "has_style_guide": {
            "type": "boolean"
          },
          "style_guide": {
            "type": "object",
            "nullable": true,
            "properties": {
              "slug": {
                "type": "string"
              },
              "overview": {
                "type": "string"
              },
              "keywords": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "techniques": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "specifications_count": {
                "type": "integer"
              },
              "films_count": {
                "type": "integer"
              }
            }
          }
        }
      },
      "ActorDetail": {
        "type": "object",
        "description": "Full actor profile with complete filmography and style guide.",
        "properties": {
          "name": {
            "type": "string"
          },
          "tmdb_id": {
            "type": "integer"
          },
          "imdb_id": {
            "type": "string",
            "nullable": true
          },
          "visual_description": {
            "type": "string"
          },
          "headshot": {
            "type": "string",
            "nullable": true
          },
          "headshot_url": {
            "type": "string",
            "nullable": true
          },
          "profile_path": {
            "type": "string",
            "nullable": true
          },
          "additional_images": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "biography": {
            "type": "string"
          },
          "birthday": {
            "type": "string",
            "nullable": true
          },
          "deathday": {
            "type": "string",
            "nullable": true
          },
          "place_of_birth": {
            "type": "string",
            "nullable": true
          },
          "gender": {
            "type": "integer"
          },
          "popularity": {
            "type": "number"
          },
          "homepage": {
            "type": "string",
            "nullable": true
          },
          "top_films": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "year": {
                  "type": "string"
                },
                "character": {
                  "type": "string"
                },
                "rating": {
                  "type": "number"
                },
                "tmdb_id": {
                  "type": "integer"
                },
                "poster_url": {
                  "type": "string",
                  "nullable": true
                },
                "genre_ids": {
                  "type": "array",
                  "items": {
                    "type": "integer"
                  }
                }
              }
            }
          },
          "also_directed": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "career_stats": {
            "type": "object",
            "nullable": true
          },
          "external_ids": {
            "type": "object",
            "nullable": true
          },
          "has_style_guide": {
            "type": "boolean"
          },
          "style_guide": {
            "type": "object",
            "nullable": true,
            "properties": {
              "slug": {
                "type": "string"
              },
              "overview": {
                "type": "string"
              },
              "keywords": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "techniques": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "films": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string"
                    },
                    "year": {
                      "type": "integer"
                    }
                  }
                }
              },
              "specifications": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Total matching records."
          },
          "offset": {
            "type": "integer",
            "description": "Current offset."
          },
          "limit": {
            "type": "integer",
            "description": "Page size."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "enum": [
                  "api_error",
                  "authentication_error",
                  "authorization_error",
                  "scope_error",
                  "rate_limit_error",
                  "not_found"
                ]
              }
            }
          }
        }
      },
      "CanonicalPoster": {
        "type": "object",
        "description": "Poster URLs. `source: gcs` resolves to the Hollywood Metrics GCS bucket; `source: tmdb` is the TMDB CDN; `source: none` means no poster is available.",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "example": "https://storage.googleapis.com/hollywood-metrics-data-latentsmurf/assets/posters/Inception.jpg"
          },
          "thumb_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "example": "https://storage.googleapis.com/hollywood-metrics-data-latentsmurf/assets/posters-mini/Inception.jpg"
          },
          "source": {
            "type": "string",
            "enum": [
              "gcs",
              "tmdb",
              "none"
            ]
          },
          "ai_analysis_available": {
            "type": "boolean",
            "description": "If true, GET /api/v2/films/{id}/poster-analysis will return AI-derived poster metadata."
          }
        },
        "required": [
          "url",
          "thumb_url",
          "source",
          "ai_analysis_available"
        ]
      },
      "CanonicalCastMember": {
        "type": "object",
        "properties": {
          "tmdb_id": {
            "type": "integer",
            "nullable": true
          },
          "name": {
            "type": "string",
            "example": "Leonardo DiCaprio"
          },
          "character": {
            "type": "string",
            "example": "Cobb"
          },
          "order": {
            "type": "integer",
            "example": 0
          },
          "headshot_url": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "actor_slug": {
            "type": "string",
            "example": "leonardo-dicaprio"
          }
        },
        "required": [
          "name",
          "character",
          "order",
          "actor_slug"
        ]
      },
      "CanonicalCrewMember": {
        "type": "object",
        "properties": {
          "tmdb_id": {
            "type": "integer",
            "nullable": true
          },
          "name": {
            "type": "string",
            "example": "Christopher Nolan"
          },
          "job": {
            "type": "string",
            "example": "Director"
          },
          "department": {
            "type": "string",
            "example": "directing"
          }
        },
        "required": [
          "name",
          "job",
          "department"
        ]
      },
      "CanonicalFilm": {
        "type": "object",
        "description": "Canonical Film object. Returned by all v2 film, recommendation, and similar endpoints.",
        "properties": {
          "imdb_id": {
            "type": "string",
            "example": "tt1375666"
          },
          "tmdb_id": {
            "type": "integer",
            "nullable": true,
            "example": 27205
          },
          "title": {
            "type": "string",
            "example": "Inception"
          },
          "year": {
            "type": "integer",
            "nullable": true,
            "example": 2010
          },
          "release_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "runtime": {
            "type": "integer",
            "nullable": true,
            "example": 148
          },
          "language": {
            "type": "string",
            "nullable": true,
            "example": "en"
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "Action",
              "Science Fiction"
            ]
          },
          "tagline": {
            "type": "string",
            "nullable": true
          },
          "overview": {
            "type": "string",
            "nullable": true
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "poster": {
            "$ref": "#/components/schemas/CanonicalPoster"
          },
          "backdrop": {
            "type": "object",
            "nullable": true,
            "properties": {
              "url": {
                "type": "string",
                "format": "uri",
                "nullable": true
              }
            }
          },
          "ratings": {
            "type": "object",
            "properties": {
              "imdb": {
                "type": "number",
                "nullable": true
              },
              "imdb_votes": {
                "type": "integer",
                "nullable": true
              },
              "rt_critics": {
                "type": "number",
                "nullable": true
              },
              "rt_audience": {
                "type": "number",
                "nullable": true
              },
              "tmdb": {
                "type": "number",
                "nullable": true
              },
              "master_score": {
                "type": "number",
                "nullable": true
              }
            }
          },
          "box_office": {
            "type": "object",
            "properties": {
              "budget": {
                "type": "number",
                "nullable": true
              },
              "worldwide_gross": {
                "type": "number",
                "nullable": true
              },
              "roi": {
                "type": "number",
                "nullable": true
              },
              "budget_tier": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "awards": {
            "type": "object",
            "properties": {
              "oscar_noms": {
                "type": "integer"
              },
              "oscar_wins": {
                "type": "integer"
              },
              "golden_globe_noms": {
                "type": "integer"
              },
              "golden_globe_wins": {
                "type": "integer"
              }
            }
          },
          "director": {
            "type": "string",
            "nullable": true
          },
          "cast": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CanonicalCastMember"
            }
          },
          "crew_summary": {
            "type": "object",
            "description": "Crew grouped by department. List mode returns only key roles; detail mode returns full crew.",
            "properties": {
              "directing": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CanonicalCrewMember"
                }
              },
              "writing": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CanonicalCrewMember"
                }
              },
              "camera": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CanonicalCrewMember"
                }
              },
              "editing": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CanonicalCrewMember"
                }
              },
              "sound": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CanonicalCrewMember"
                }
              },
              "art": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CanonicalCrewMember"
                }
              },
              "production": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CanonicalCrewMember"
                }
              },
              "other": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CanonicalCrewMember"
                }
              }
            }
          },
          "production_companies": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "logo_url": {
                  "type": "string",
                  "format": "uri",
                  "nullable": true
                }
              }
            }
          },
          "watch_providers": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          },
          "external_ids": {
            "type": "object",
            "properties": {
              "imdb_id": {
                "type": "string"
              },
              "tmdb_id": {
                "type": "integer",
                "nullable": true
              }
            }
          },
          "links": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              },
              "similar": {
                "type": "string"
              },
              "poster_analysis": {
                "type": "string",
                "nullable": true
              }
            }
          }
        },
        "required": [
          "imdb_id",
          "title",
          "poster",
          "ratings",
          "box_office",
          "awards",
          "cast",
          "crew_summary",
          "links"
        ]
      },
      "V2PaginationMeta": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque token. Pass as ?cursor= to fetch the next page."
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "PosterAnalysis": {
        "type": "object",
        "properties": {
          "imdb_id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "year": {
            "type": "integer"
          },
          "analysis": {
            "type": "object",
            "description": "AI-derived poster descriptors. Field names expanded from the underlying poster_ai.json compact schema.",
            "properties": {
              "design_quality": {
                "type": "integer",
                "description": "1-10"
              },
              "mood": {
                "type": "string"
              },
              "atmosphere": {
                "type": "string"
              },
              "composition": {
                "type": "string"
              },
              "visual_style": {
                "type": "string"
              },
              "brightness": {
                "type": "string"
              },
              "contrast": {
                "type": "string"
              },
              "saturation": {
                "type": "string"
              },
              "dominant_colors": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Hex codes"
              },
              "genre_signals": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "era": {
                "type": "string"
              },
              "setting": {
                "type": "string"
              },
              "typography": {
                "type": "string"
              },
              "tagline": {
                "type": "string"
              },
              "visual_description": {
                "type": "string"
              },
              "key_objects": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "expressions": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "additionalProperties": true
          }
        }
      },
      "RecommendV2Request": {
        "type": "object",
        "required": [
          "liked"
        ],
        "properties": {
          "liked": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "IMDb IDs or film titles. Titles are fuzzy-matched."
          },
          "disliked": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "limit": {
            "type": "integer",
            "default": 20,
            "minimum": 1,
            "maximum": 100
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": 401,
                "message": "Missing API key. Include X-API-Key header or api_key query parameter.",
                "type": "authentication_error"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": 404,
                "message": "Resource not found.",
                "type": "not_found"
              }
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request parameters.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": 400,
                "message": "Invalid request parameters.",
                "type": "api_error"
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded. Limits reset at 00:00 UTC daily.",
        "headers": {
          "X-RateLimit-Limit": {
            "description": "Daily request limit for your tier.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "description": "Requests remaining today.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "description": "ISO 8601 timestamp when the limit resets.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          "Retry-After": {
            "description": "Seconds until the rate limit resets.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": 429,
                "message": "Rate limit exceeded. Limits reset at 00:00 UTC daily.",
                "type": "rate_limit_error"
              }
            }
          }
        }
      }
    }
  }
}