{"openapi":"3.1.0","info":{"title":"Podcast Generator API","description":"API для автоматической генерации подкастов из текстовых материалов","version":"0.1.0"},"paths":{"/api/auth/login":{"get":{"tags":["auth"],"summary":"Start OIDC login","operationId":"login_api_auth_login_get","parameters":[{"name":"next","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/callback":{"get":{"tags":["auth"],"summary":"OIDC callback (code exchange)","operationId":"callback_api_auth_callback_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Description"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/logout":{"get":{"tags":["auth"],"summary":"Start OIDC logout","operationId":"logout_api_auth_logout_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/logout/callback":{"get":{"tags":["auth"],"summary":"OIDC logout callback","operationId":"logout_callback_api_auth_logout_callback_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/me":{"get":{"tags":["auth"],"summary":"Current authenticated user","description":"Returns user info from access_token cookie. 401 if missing/invalid.","operationId":"me_api_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMeResponse"}}}}}}},"/api/v1/files":{"post":{"tags":["files"],"summary":"Upload source files","description":"Upload up to 20 files (TXT, DOCX, PPTX, PDF, JPG, PNG) for podcast generation.","operationId":"upload_files_api_v1_files_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_files_api_v1_files_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FileUploadResponse"},"type":"array","title":"Response Upload Files Api V1 Files Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/podcasts":{"post":{"tags":["podcasts"],"summary":"Create podcast — Phase 1 (script generation)","description":"Phase 1 — creates podcast and starts LLM script generation in background.","operationId":"create_podcast_api_v1_podcasts_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodcastCreateRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodcastResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/podcasts/{podcast_id}":{"get":{"tags":["podcasts"],"summary":"Get podcast details","operationId":"get_podcast_api_v1_podcasts__podcast_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"podcast_id","in":"path","required":true,"schema":{"type":"integer","title":"Podcast Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodcastResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/podcasts/{podcast_id}/script":{"patch":{"tags":["podcasts"],"summary":"Edit script + SSML","description":"Save new script version (supports SSML tags).","operationId":"update_script_api_v1_podcasts__podcast_id__script_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"podcast_id","in":"path","required":true,"schema":{"type":"integer","title":"Podcast Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/podcasts/{podcast_id}/synthesize":{"post":{"tags":["podcasts"],"summary":"Synthesize podcast — Phase 2 (validation + TTS)","description":"Phase 2 — validates script via LLM and synthesizes audio.","operationId":"synthesize_podcast_api_v1_podcasts__podcast_id__synthesize_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"podcast_id","in":"path","required":true,"schema":{"type":"integer","title":"Podcast Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynthesizeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodcastResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/podcasts/{podcast_id}/translate":{"post":{"tags":["podcasts"],"summary":"Translate script via LLM","description":"Translate current script to target language via LLM (preserves SSML).","operationId":"translate_script_api_v1_podcasts__podcast_id__translate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"podcast_id","in":"path","required":true,"schema":{"type":"integer","title":"Podcast Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/podcasts/{podcast_id}/versions":{"get":{"tags":["podcasts"],"summary":"Get script version history","operationId":"get_versions_api_v1_podcasts__podcast_id__versions_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"podcast_id","in":"path","required":true,"schema":{"type":"integer","title":"Podcast Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScriptVersionResponse"},"title":"Response Get Versions Api V1 Podcasts  Podcast Id  Versions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/podcasts/{podcast_id}/audio":{"get":{"tags":["podcasts"],"summary":"Get audio download URL","operationId":"get_audio_api_v1_podcasts__podcast_id__audio_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"podcast_id","in":"path","required":true,"schema":{"type":"integer","title":"Podcast Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioURLResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/languages":{"get":{"tags":["languages"],"summary":"List supported languages","description":"Return all supported languages with TTS availability info.","operationId":"list_languages_api_v1_languages_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/LanguageResponse"},"type":"array","title":"Response List Languages Api V1 Languages Get"}}}}}}},"/api/v1/admin/prompts":{"get":{"tags":["admin"],"summary":"List all prompts","operationId":"list_prompts_api_v1_admin_prompts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PromptResponse"},"type":"array","title":"Response List Prompts Api V1 Admin Prompts Get"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["admin"],"summary":"Create prompt","operationId":"create_prompt_api_v1_admin_prompts_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptCreateRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/admin/prompts/{prompt_id}":{"put":{"tags":["admin"],"summary":"Update prompt","operationId":"update_prompt_api_v1_admin_prompts__prompt_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"prompt_id","in":"path","required":true,"schema":{"type":"integer","title":"Prompt Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Deactivate prompt","operationId":"delete_prompt_api_v1_admin_prompts__prompt_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"prompt_id","in":"path","required":true,"schema":{"type":"integer","title":"Prompt Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users":{"get":{"tags":["admin"],"summary":"List users","operationId":"list_users_api_v1_admin_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UserAdminResponse"},"type":"array","title":"Response List Users Api V1 Admin Users Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/admin/users/{user_id}/block":{"put":{"tags":["admin"],"summary":"Block/unblock user","operationId":"block_user_api_v1_admin_users__user_id__block_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserBlockRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserAdminResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/podcasts":{"get":{"tags":["admin"],"summary":"List all podcasts","operationId":"list_all_podcasts_api_v1_admin_podcasts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PodcastResponse"},"type":"array","title":"Response List All Podcasts Api V1 Admin Podcasts Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/admin/podcasts/{podcast_id}":{"delete":{"tags":["admin"],"summary":"Delete podcast","operationId":"delete_podcast_api_v1_admin_podcasts__podcast_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"podcast_id","in":"path","required":true,"schema":{"type":"integer","title":"Podcast Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/stats":{"get":{"tags":["admin"],"summary":"Get system statistics","description":"Returns aggregated stats:\n- Total podcasts / by status\n- Active users\n- Processing times\n- Top languages & LLM models\n- (v2: CSV export, Redis caching)","operationId":"get_stats_api_v1_admin_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/health":{"get":{"tags":["infra"],"summary":"Health","description":"Liveness probe.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Health Health Get"}}}}}}},"/health/ready":{"get":{"tags":["infra"],"summary":"Readiness","description":"Readiness probe — checks DB, Redis, MinIO connectivity.","operationId":"readiness_health_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Readiness Health Ready Get"}}}}}}}},"components":{"schemas":{"AudioURLResponse":{"properties":{"podcast_id":{"type":"integer","title":"Podcast Id"},"url":{"type":"string","title":"Url"},"format":{"type":"string","title":"Format"}},"type":"object","required":["podcast_id","url","format"],"title":"AudioURLResponse"},"Body_upload_files_api_v1_files_post":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files"}},"type":"object","required":["files"],"title":"Body_upload_files_api_v1_files_post"},"FileUploadResponse":{"properties":{"file_id":{"type":"string","title":"File Id"},"filename":{"type":"string","title":"Filename"},"size_bytes":{"type":"integer","title":"Size Bytes"},"content_type":{"type":"string","title":"Content Type"},"text_extracted":{"type":"boolean","title":"Text Extracted"},"partial_warnings":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Partial Warnings"}},"type":"object","required":["file_id","filename","size_bytes","content_type","text_extracted"],"title":"FileUploadResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LLMModelEnum":{"type":"string","enum":["qwen","yandexgpt"],"title":"LLMModelEnum"},"LanguageResponse":{"properties":{"code":{"type":"string","title":"Code"},"name":{"type":"string","title":"Name"},"is_default":{"type":"boolean","title":"Is Default"},"tts_supported":{"type":"boolean","title":"Tts Supported"}},"type":"object","required":["code","name","is_default","tts_supported"],"title":"LanguageResponse"},"PodcastCreateRequest":{"properties":{"file_ids":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"File Ids","description":"Uploaded file IDs","default":[]},"source_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Text","description":"Direct text input (alternative to files)"},"processing_depth":{"$ref":"#/components/schemas/ProcessingDepthEnum","description":"raw = censorship + grammar + restructure; full = creative podcast script","default":"full"},"language":{"type":"string","maxLength":10,"title":"Language","default":"ru"},"llm_model":{"anyOf":[{"$ref":"#/components/schemas/LLMModelEnum"},{"type":"null"}],"default":"qwen"},"title":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Title"}},"type":"object","title":"PodcastCreateRequest","description":"POST /podcasts — start script generation (Phase 1)."},"PodcastResponse":{"properties":{"id":{"type":"integer","title":"Id"},"user_id":{"type":"string","title":"User Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"status":{"type":"string","title":"Status"},"processing_depth":{"type":"string","title":"Processing Depth"},"language":{"type":"string","title":"Language"},"llm_model":{"type":"string","title":"Llm Model"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","user_id","title","status","processing_depth","language","llm_model","error_message","created_at","updated_at"],"title":"PodcastResponse"},"ProcessingDepthEnum":{"type":"string","enum":["raw","full"],"title":"ProcessingDepthEnum","description":"Two-level processing mode.\n\n- raw: цензура + грамматика + реструктуризация (без творческой переработки)\n- full: полный сценарий с интро, связками, outro"},"PromptCreateRequest":{"properties":{"name":{"type":"string","maxLength":200,"title":"Name"},"template":{"type":"string","title":"Template"},"language":{"type":"string","maxLength":10,"title":"Language","default":"ru"}},"type":"object","required":["name","template"],"title":"PromptCreateRequest"},"PromptResponse":{"properties":{"id":{"type":"integer","title":"Id"},"name":{"type":"string","title":"Name"},"template":{"type":"string","title":"Template"},"language":{"type":"string","title":"Language"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","name","template","language","is_active","created_at","updated_at"],"title":"PromptResponse"},"PromptUpdateRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name"},"template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template"},"language":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Language"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"PromptUpdateRequest"},"ScriptUpdateRequest":{"properties":{"content":{"type":"string","minLength":1,"title":"Content","description":"Updated script content (may include SSML)"},"language":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Language"}},"type":"object","required":["content"],"title":"ScriptUpdateRequest","description":"PATCH /podcasts/{id}/script — edit script + SSML."},"ScriptVersionResponse":{"properties":{"id":{"type":"integer","title":"Id"},"podcast_id":{"type":"integer","title":"Podcast Id"},"version":{"type":"integer","title":"Version"},"content":{"type":"string","title":"Content"},"language":{"type":"string","title":"Language"},"translated_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Translated From"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","podcast_id","version","content","language","translated_from","created_at"],"title":"ScriptVersionResponse"},"StatsResponse":{"properties":{"total_podcasts":{"type":"integer","title":"Total Podcasts"},"podcasts_by_status":{"additionalProperties":{"type":"integer"},"type":"object","title":"Podcasts By Status"},"total_users":{"type":"integer","title":"Total Users"},"podcasts_today":{"type":"integer","title":"Podcasts Today"},"average_processing_time_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Average Processing Time Seconds"},"top_languages":{"additionalProperties":{"type":"integer"},"type":"object","title":"Top Languages"},"top_llm_models":{"additionalProperties":{"type":"integer"},"type":"object","title":"Top Llm Models"}},"type":"object","required":["total_podcasts","podcasts_by_status","total_users","podcasts_today","average_processing_time_seconds","top_languages","top_llm_models"],"title":"StatsResponse"},"SynthesizeRequest":{"properties":{"voice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Voice","description":"TTS voice ID"},"speed":{"type":"number","maximum":2.0,"minimum":0.5,"title":"Speed","default":1.0},"audio_format":{"type":"string","pattern":"^(mp3|wav)$","title":"Audio Format","default":"mp3"},"sample_rate":{"type":"integer","title":"Sample Rate","default":22050},"bitrate":{"type":"integer","title":"Bitrate","default":128}},"type":"object","title":"SynthesizeRequest","description":"POST /podcasts/{id}/synthesize — Phase 2: validate + TTS."},"TranslateRequest":{"properties":{"target_language":{"type":"string","maxLength":10,"title":"Target Language","description":"Target language code"}},"type":"object","required":["target_language"],"title":"TranslateRequest","description":"POST /podcasts/{id}/translate — translate script via LLM."},"UserAdminResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"is_blocked":{"type":"boolean","title":"Is Blocked"},"podcast_count":{"type":"integer","title":"Podcast Count"},"last_activity":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity"}},"type":"object","required":["user_id","is_blocked","podcast_count","last_activity"],"title":"UserAdminResponse"},"UserBlockRequest":{"properties":{"is_blocked":{"type":"boolean","title":"Is Blocked"}},"type":"object","required":["is_blocked"],"title":"UserBlockRequest"},"UserMeResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"preferred_username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preferred Username"},"roles":{"items":{"type":"string"},"type":"array","title":"Roles","default":[]}},"type":"object","required":["user_id"],"title":"UserMeResponse","description":"Current authenticated user info, derived from access_token cookie claims."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}