{"openapi":"3.1.0","info":{"title":"AI Shorts Aggregator API","version":"1.1.0","description":"Aggregated short-drama catalog: providers → series → episodes. All responses use the envelope `{ success, data, error, meta }`. Catalog endpoints require `X-API-Key` when the server runs with REQUIRE_API_KEY=true; create a key at /signup → /dashboard. `playback.url` for hosted/stream episodes is a signed, expiring URL that works without a key."},"servers":[{"url":"http://localhost:8011"}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Self-serve key from the dashboard (sk_live_…)"},"adminKey":{"type":"apiKey","in":"header","name":"X-Admin-Key"},"session":{"type":"http","scheme":"bearer","description":"Session token from /v1/auth/signup or /v1/auth/login"}}},"security":[{"apiKey":[]}],"paths":{"/v1/health":{"get":{"tags":["meta"],"summary":"Health check","security":[],"responses":{"200":{"description":"OK"}}}},"/v1/auth/signup":{"post":{"security":[],"tags":["auth"],"summary":"Create an account (rate limited 10/min per IP)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8,"maxLength":128},"name":{"type":"string","minLength":1,"maxLength":80}}}}}},"responses":{"201":{"description":"{ user, token, expiresAt }"},"409":{"description":"email_taken"}}}},"/v1/auth/login":{"post":{"security":[],"tags":["auth"],"summary":"Log in (rate limited 10/min per IP)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8,"maxLength":128}}}}}},"responses":{"200":{"description":"{ user, token, expiresAt }"},"401":{"description":"invalid_credentials"}}}},"/v1/auth/logout":{"post":{"security":[{"session":[]}],"tags":["auth"],"summary":"Invalidate the current session","responses":{"200":{"description":"{ loggedOut: true }"}}}},"/v1/auth/me":{"get":{"security":[{"session":[]}],"tags":["auth"],"summary":"Current user","responses":{"200":{"description":"{ user }"}}}},"/v1/account/keys":{"get":{"security":[{"session":[]}],"tags":["account"],"summary":"List your API keys (newest first, incl. revoked) with usage","responses":{"200":{"description":"ApiKey[]"}}},"post":{"security":[{"session":[]}],"tags":["account"],"summary":"Create an API key (max 10 active). The secret is returned once.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":60}}}}}},"responses":{"201":{"description":"{ key, secret }"},"409":{"description":"key_limit"}}}},"/v1/account/keys/{id}":{"delete":{"security":[{"session":[]}],"tags":["account"],"summary":"Revoke an API key","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"{ revoked: true }"}}}},"/v1/account/usage":{"get":{"security":[{"session":[]}],"tags":["account"],"summary":"Daily request counts across your keys (UTC, zero-filled, oldest first)","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":90,"default":30}}],"responses":{"200":{"description":"{ total, days: [{ day, count }] }"}}}},"/v1/providers":{"get":{"tags":["catalog"],"summary":"List enabled providers","responses":{"200":{"description":"Providers"}}}},"/v1/providers/{slug}":{"get":{"tags":["catalog"],"summary":"Get provider","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Provider"}}}},"/v1/providers/{slug}/series":{"get":{"tags":["catalog"],"summary":"List a provider's series","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"genre","in":"query","required":false,"schema":{"type":"string"}},{"name":"language","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ongoing","completed","unknown"]}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["newest","oldest","popular","rating","title"],"default":"newest"}}],"responses":{"200":{"description":"Series page"}}}},"/v1/series":{"get":{"tags":["catalog"],"summary":"Search/list series across providers","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"provider","in":"query","required":false,"schema":{"type":"string"}},{"name":"genre","in":"query","required":false,"schema":{"type":"string"}},{"name":"language","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ongoing","completed","unknown"]}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["newest","oldest","popular","rating","title"],"default":"newest"}}],"responses":{"200":{"description":"Series page"}}}},"/v1/series/{id}":{"get":{"tags":["catalog"],"summary":"Get series","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Series"}}}},"/v1/series/{id}/episodes":{"get":{"tags":["catalog"],"summary":"List episodes of a series","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"default":100}}],"responses":{"200":{"description":"Episodes page"}}}},"/v1/episodes/{id}":{"get":{"tags":["catalog"],"summary":"Get episode (with signed playback URL)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Episode"}}}},"/v1/episodes/{id}/stream":{"get":{"tags":["playback"],"summary":"Play episode video","description":"Authorize with X-API-Key or the signed `exp`/`sig` query from `playback.url`. hosted → streams the file (HTTP Range supported); stream → 302 to the licensed stream URL; external → 409 with provider link; none → 404.","security":[{"apiKey":[]},{}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"exp","in":"query","required":false,"schema":{"type":"integer"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Full file"},"206":{"description":"Partial content"},"302":{"description":"Redirect to stream"},"403":{"description":"Invalid or expired signature"}}}},"/v1/genres":{"get":{"tags":["catalog"],"summary":"List genres with series counts","responses":{"200":{"description":"Genres"}}}},"/v1/admin/connectors":{"get":{"security":[{"adminKey":[]}],"tags":["admin"],"summary":"List registered connectors","responses":{"200":{"description":"Connectors"}}}},"/v1/admin/providers":{"get":{"security":[{"adminKey":[]}],"tags":["admin"],"summary":"List all providers incl. disabled and connector config","responses":{"200":{"description":"Providers"}}},"put":{"security":[{"adminKey":[]}],"tags":["admin"],"summary":"Create or replace a provider","responses":{"200":{"description":"Provider"}}}},"/v1/admin/providers/{slug}":{"patch":{"security":[{"adminKey":[]}],"tags":["admin"],"summary":"Update provider fields","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Provider"}}}},"/v1/admin/providers/{slug}/series":{"put":{"security":[{"adminKey":[]}],"tags":["admin"],"summary":"Upsert one series with episodes (manual/partner push)","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Stored series"}}}},"/v1/admin/providers/{slug}/sync":{"post":{"security":[{"adminKey":[]}],"tags":["admin"],"summary":"Start a connector sync","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Sync started"}}}},"/v1/admin/sync-runs":{"get":{"security":[{"adminKey":[]}],"tags":["admin"],"summary":"Recent sync runs","responses":{"200":{"description":"Sync runs"}}}},"/v1/admin/series/{id}":{"delete":{"security":[{"adminKey":[]}],"tags":["admin"],"summary":"Delete a series","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted"}}}}}}