# Aurora Core — API Base path: `/api/v1`. Interactive OpenAPI docs are available at `/docs` in any environment where `AURORA_API_DOCS_ENABLED=true` (default; set to `false` to hide docs in production — section 32). All responses are JSON, built from Pydantic response schemas (`app/schemas/`), never from serialized ORM objects (section 18). Errors use a consistent envelope: ```json { "error": { "code": "movie_not_found", "message": "No such movie: aur_movie_..." } } ``` ## Health | Method | Path | Notes | |---|---|---| | GET | `/health` and `/api/v1/health` | Liveness only. | | GET | `/ready` and `/api/v1/ready` | Checks PostgreSQL (always) and Redis (if `REDIS_REQUIRED=true`). | ## Search | Method | Path | Notes | |---|---|---| | GET | `/api/v1/search?q=...&limit=10` | Searches movies, series, people, collections, companies. `limit` (1-50) caps results **per type**. | ```json GET /api/v1/search?q=the%20matrix { "query": "the matrix", "movies": [ { "id": "aur_movie_...", "type": "movie", "title": "The Matrix", "release_date": "1999-03-30", "poster_path": "https://..." } ], "series": [], "people": [], "collections": [ { "id": "aur_collection_...", "name": "The Matrix Collection" } ], "companies": [], "total_results": 2 } ``` ## Movies | Method | Path | |---|---| | GET | `/api/v1/movies/{aurora_id}` | | GET | `/api/v1/movies/{aurora_id}/credits` | | GET | `/api/v1/movies/{aurora_id}/images` | | GET | `/api/v1/movies/{aurora_id}/videos` | `GET /api/v1/movies/{aurora_id}` (cached, see `docs/architecture.md#caching`): ```json { "id": "aur_movie_01m1q7p1c...", "type": "movie", "title": "The Matrix", "original_title": "The Matrix", "overview": "...", "tagline": "Welcome to the Real World", "status": "released", "original_language": "en", "release_date": "1999-03-30", "runtime": 136, "budget": 63000000, "revenue": 463517383, "popularity": 45.5, "vote_average": 8.2, "vote_count": 24000, "adult": false, "genres": [{ "id": "aur_genre_...", "name": "Action" }], "keywords": [{ "id": "aur_keyword_...", "name": "dystopia" }], "cast": [{ "id": "aur_credit_...", "person": { "id": "aur_person_...", "name": "Keanu Reeves", "profile_path": null }, "character": "Neo", "billing_order": 0 }], "crew": [{ "id": "aur_credit_...", "person": { "id": "aur_person_...", "name": "Lana Wachowski" }, "department": "Directing", "job": "Director" }], "companies": [{ "id": "aur_company_...", "name": "Village Roadshow Pictures", "company_type": "production_company" }], "collection": { "id": "aur_collection_...", "name": "The Matrix Collection" }, "images": { "posters": [...], "backdrops": [...], "logos": [...] }, "videos": [{ "id": "aur_vid_...", "type": "trailer", "site": "YouTube", "url": "https://www.youtube.com/watch?v=...", "is_official": true }], "external_ids": { "tmdb": "603", "imdb": "tt0133093" }, "created_at": "...", "updated_at": "..." } ``` ## Series / Seasons / Episodes | Method | Path | |---|---| | GET | `/api/v1/series/{aurora_id}` | | GET | `/api/v1/series/{aurora_id}/seasons` | | GET | `/api/v1/seasons/{aurora_id}` | | GET | `/api/v1/episodes/{aurora_id}` | Series detail includes `networks` and `companies` as separate lists (a TV network and a production company are both `Company` rows distinguished by `company_type` — see `docs/database.md`), plus `creators` (series-level "Creator" credits) alongside `cast`/`crew`. ## People | Method | Path | |---|---| | GET | `/api/v1/people/{aurora_id}` | | GET | `/api/v1/people/{aurora_id}/credits` (movie filmography in 0.1; series/episode credits are stored but not yet surfaced here — see Known limitations) | ## Collections / Companies | Method | Path | |---|---| | GET | `/api/v1/collections/{aurora_id}` | | GET | `/api/v1/companies/{aurora_id}` | ## Administrative (section 17: "separately protected") Require header `X-API-Key: `. Never exposed to public clients — mount behind a separate Traefik router/ACL in production if you want network-level isolation too. | Method | Path | Body | Notes | |---|---|---|---| | POST | `/api/v1/admin/ingest/movie` | `{"tmdb_id": "603"}` | Enqueues an arq job; returns `{"job_id": "...", "status": "enqueued"}` immediately (section 15: ingestion never runs inline in a request). | | POST | `/api/v1/admin/ingest/person` | `{"tmdb_id": "6384"}` | Same, for a person profile. | | POST | `/api/v1/admin/ingest/series` | `{"tmdb_id": "1396"}` | Ingests the series and season *stubs* (number, title, air date, poster) — no episodes yet. | | POST | `/api/v1/admin/ingest/season` | `{"tmdb_id": "1396", "season_number": 1}` | Ingests one season's full episode list. Requires the series to already be ingested (`ingest/series` first) — fails with a clear error otherwise. | | GET | `/api/v1/admin/ingest/jobs/{job_id}` | — | Poll job status: `pending` → `running` → `succeeded`/`failed`/`retrying`. | ## Errors | Status | When | |---|---| | 404 | Entity not found, or the ID is well-formed but the wrong entity type (`aur_person_...` passed to `/movies/{id}`) | | 422 | Request validation failure (e.g. missing/invalid query params) | | 429 | Rate limit exceeded (`Retry-After` header set) | | 401 | Missing/invalid `X-API-Key` on an admin endpoint | | 503 | Admin API called with `AURORA_ADMIN_API_KEYS` unset, or the job queue is unavailable | ## Known limitations - No pagination on list-shaped responses yet beyond search's `limit` (movie/series lists like recommendations return everything). Cursor-based pagination using the Aurora ID's inherent creation-order sort (see `docs/database.md#aurora-ids-vs-primary-keys`) is the natural next step. - `/people/{id}/credits` only returns movie credits today; series/episode credits exist in the `credits` table but the response assembly for them is not yet wired up (`app/services/person_service.py`).