{"openapi":"3.1.0","info":{"title":"Text to speech API service","description":"\n    Castle AI's Text-to-Speech generation service enables speech synthesis\n    using predefined voices and voice cloning technology.\n    ","version":"0.1.0"},"paths":{"/metrics":{"get":{"summary":"Metrics","description":"Expose Prometheus metrics in the format expected by a Prometheus scrape target.\n\nReturns:\n    Response: Raw metrics payload with the appropriate Prometheus content-type header.","operationId":"metrics_metrics_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Index","operationId":"index__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health_check":{"get":{"summary":"Health Check","description":"Liveness health check.\n\nWhen the ``ADVANCED_HEALTH_CHECK`` environment variable is ``\"true\"``, performs a\nfull connectivity check against Redis and PostgreSQL and returns a composite status\nobject.  Otherwise returns a minimal ``{\"status\": \"ok\"}`` ping response immediately,\nkeeping the endpoint lightweight for load-balancer probes.\n\nReturns:\n    JSONResponse: 200 when all checked services are healthy, 500 otherwise.","operationId":"health_check_health_check_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health_check/ready":{"get":{"summary":"Readiness","description":"Readiness check — always performs a full connectivity probe.\n\nUnlike the liveness endpoint, this check always runs all connectivity assertions\n(Redis ping, PostgreSQL query) regardless of ``ADVANCED_HEALTH_CHECK``.\nIntended for Kubernetes readiness probes that gate traffic on full stack availability.\n\nReturns:\n    JSONResponse: 200 when all services are reachable, 500 if any check fails.","operationId":"readiness_health_check_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/tokens/verify":{"get":{"tags":["Tokens"],"summary":"Verify Token","description":"Verify an API key and return the associated token record.\n\nOnly ``sk``-prefixed (single-use) tokens are supported.  The raw key is hashed\nand looked up in the database; the hash stored in the record must match to prevent\ntoken enumeration.\n\nArgs:\n    api_key: Raw API key supplied as a query parameter.\n    token_service: Injected token service for database look-ups.\n\nReturns:\n    TokenOut: Validated token record.\n\nRaises:\n    InvalidAPIKey: When the key prefix is unrecognised, the token is not found,\n        or the stored hash does not match.","operationId":"verify_token_tokens_verify_get","parameters":[{"name":"api_key","in":"query","required":true,"schema":{"type":"string","title":"Api Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/text_to_speech/streaming":{"post":{"tags":["Streaming"],"summary":"Stream Text To Speech","description":"Stream synthesised speech as Server-Sent Events (SSE).\n\nAuthenticates the caller, checks quota, and delegates synthesis to the versioned\nhandler registered in ``voice_registry[\"streaming_text_to_speech\"]``.  Concurrency\nis enforced per-token via Redis before the handler is invoked.\n\nArgs:\n    tts_request: TTS parameters (text, voice, model, format, etc.).\n    background_tasks: FastAPI background-task queue (passed through to the handler).\n    token_service: Injected token validation service.\n    _request: Raw FastAPI ``Request`` object used for rate-limiting and ``app.state`` access.\n    version: API version extracted from the ``version`` request header.\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n\nReturns:\n    StreamingResponse: SSE stream of audio chunks.\n\nRaises:\n    UsageLimitExceeded: When the token has exhausted its quota.\n    TooManyConcurrentRequests: When the per-token concurrency cap is reached.","operationId":"stream_text_to_speech_text_to_speech_streaming_post","parameters":[{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"429":{"description":"Too Many Requests / Concurrency / QUOTA EXCEEDED","content":{"application/json":{"example":{"error_code":"TOO_MANY_CONCURRENT_REQUESTS","message":"Too many concurrent requests","extra":{"limit":5,"current":6}},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/text_to_speech/streaming/hidden/playground":{"post":{"tags":["Streaming"],"summary":"Stream Text To Speech Playground","description":"Internal playground endpoint for streaming TTS — not shown in the public schema.\n\nReads the API key from the ``PLAYGROUND_API_KEY`` environment variable and\ndelegates to :func:`stream_text_to_speech`.  Rate-limited independently via\nthe ``RATELIMIT.PLAYGROUND`` config section.\n\nArgs:\n    tts_request: TTS parameters (text, voice, model, format, etc.).\n    background_tasks: FastAPI background-task queue.\n    _request: Raw FastAPI ``Request`` object used for rate-limiting and ``app.state`` access.\n    rl: Playground-specific rate-limiter dependency.\n\nReturns:\n    StreamingResponse: SSE stream of audio chunks (same as the main endpoint).\n\nRaises:\n    Exception: If ``PLAYGROUND_API_KEY`` is not configured.","operationId":"stream_text_to_speech_playground_text_to_speech_streaming_hidden_playground_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"429":{"description":"Too Many Requests / Concurrency / QUOTA EXCEEDED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error_code":"TOO_MANY_CONCURRENT_REQUESTS","message":"Too many concurrent requests","extra":{"limit":5,"current":6}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/text_to_speech/streaming/bench":{"post":{"tags":["Streaming"],"summary":"Bench Stream","operationId":"bench_stream_text_to_speech_streaming_bench_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/text_to_speech/with_timestamps":{"post":{"tags":["Synchronous"],"summary":"Sync Text To Speech With Timestamps","description":"Synthesise speech and return audio with per-word alignment timestamps.\n\nResolves the voice preset, validates quota, then calls the versioned gRPC handler\nwith ``include_durations=True``.  Returns a ``TTSAudioResponse`` containing\nbase64-encoded audio and character-level start/end times in milliseconds.\n\nArgs:\n    tts_request: TTS parameters (text, voice, model, output format, etc.).\n    token_service: Injected token validation service.\n    _request: Raw FastAPI ``Request`` used for rate-limiting.\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    version: API version extracted from the ``version`` request header.\n    tts_service_stub: gRPC stub connected to the XCODEC synthesis server.\n\nReturns:\n    TTSAudioResponse: Base64-encoded audio and word-level alignment data.\n\nRaises:\n    NotSupportedModel: When the requested model does not support the voice.\n    VoiceNotFound: When the voice preset cannot be resolved.\n    UsageLimitExceeded: When the token has exhausted its quota.","operationId":"sync_text_to_speech_with_timestamps_text_to_speech_with_timestamps_post","parameters":[{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSAudioResponse"},"example":{"audio_base64":"base64_encoded_audio_data","alignment":{"characters":["Hello","World"],"character_start_times_milliseconds":[0.0,100.0],"character_end_times_milliseconds":[100.0,200.0]}}}}},"429":{"description":"Too Many Requests / Concurrency / QUOTA EXCEEDED","content":{"application/json":{"example":{"error_code":"TOO_MANY_CONCURRENT_REQUESTS","message":"Too many concurrent requests","extra":{"limit":5,"current":6}},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/text_to_speech":{"post":{"tags":["Synchronous"],"summary":"Sync Text To Speech","description":"Synthesise speech and return the audio file directly.\n\nValidates quota, synthesises audio via :func:`_generate_tts_bytes`, writes the\nresult to a temporary file, and returns it as a ``FileResponse``.  The temp file is\ndeleted in a background task after the response is sent.\n\nArgs:\n    tts_request: TTS parameters (text, voice, model, output format, etc.).\n    background_tasks: FastAPI background-task queue used to schedule temp-file cleanup.\n    token_service: Injected token validation service.\n    _request: Raw FastAPI ``Request`` used for rate-limiting.\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    version: API version extracted from the ``version`` request header.\n    tts_service_stub: gRPC stub connected to the XCODEC synthesis server.\n\nReturns:\n    FileResponse: Audio file in the format requested by ``tts_request.output_format``.\n\nRaises:\n    UsageLimitExceeded: When the token has exhausted its quota.","operationId":"sync_text_to_speech_text_to_speech_post","parameters":[{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSRequest"}}}},"responses":{"200":{"description":"Successful Response"},"429":{"description":"Too Many Requests / Concurrency / QUOTA EXCEEDED","content":{"application/json":{"example":{"error_code":"TOO_MANY_CONCURRENT_REQUESTS","message":"Too many concurrent requests","extra":{"limit":5,"current":6}},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/text_to_speech/with_signed_url_output":{"post":{"tags":["Synchronous"],"summary":"Sync Text To Speech With Signed Url Output","description":"Synthesise speech and return a short-lived GCS signed URL to the audio file.\n\nCalls :func:`sync_text_to_speech` internally, uploads the resulting audio to the\nconfigured GCS bucket, generates a signed download URL (TTL controlled by\n``GENERIC.sync_tts_results_expiration_time_minutes``), and returns it.\n\nArgs:\n    tts_request: TTS parameters (text, voice, model, output format, etc.).\n    background_tasks: FastAPI background-task queue used for temp-file cleanup.\n    token_service: Injected token validation service.\n    _request: Raw FastAPI ``Request`` used for rate-limiting.\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    version: API version extracted from the ``version`` request header.\n    tts_service_stub: gRPC stub connected to the XCODEC synthesis server.\n\nReturns:\n    SyncTTSWithSUAudioResponse: Signed GCS URL pointing to the synthesised audio.\n\nRaises:\n    Exception: Re-raises any error that occurs during GCS upload or URL generation.","operationId":"sync_text_to_speech_with_signed_url_output_text_to_speech_with_signed_url_output_post","parameters":[{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncTTSWithSUAudioResponse"}}}},"429":{"description":"Too Many Requests / Concurrency / QUOTA EXCEEDED","content":{"application/json":{"example":{"error_code":"TOO_MANY_CONCURRENT_REQUESTS","message":"Too many concurrent requests","extra":{"limit":5,"current":6}},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/speech_to_text/":{"post":{"tags":["Speech to Text"],"summary":"Speech To Text","description":"Transcribe an uploaded audio file (multipart/form-data).","operationId":"speech_to_text_speech_to_text__post","parameters":[{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_speech_to_text_speech_to_text__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechToTextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/speech_to_text/base64":{"post":{"tags":["Speech to Text"],"summary":"Speech To Text Base64","description":"Transcribe base64-encoded audio (application/json).","operationId":"speech_to_text_base64_speech_to_text_base64_post","parameters":[{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechToTextJSONRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechToTextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voices/prompt-audio/{voice_id}":{"get":{"tags":["Synth Voices"],"summary":"Get Voice Prompt Audio","description":"Download the original prompt audio for a voice from GCS.\n\nRetrieves the preset row from the ``voice_presets`` PostgreSQL table using the\npreset_id resolved from PostgreSQL, reads the ``prompt_audio_gcs_uri`` field (format\n``<bucket>/<filename>``), downloads the file from GCS, and returns the raw audio\nbytes with an appropriate ``Content-Type`` header derived from the file extension.\n\nArgs:\n    voice_id: UUID of the voice whose prompt audio should be fetched.\n    model_version: AI model version used to resolve the preset_id (default\n        ``async_flash_v1.0``).\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    token_service: Injected token validation service.\n    request: Raw FastAPI ``Request``.\nReturns:\n    Response: Raw audio bytes with ``Content-Disposition`` attachment header.\n\nRaises:\n    VoicePreviewNotFound: When no preset exists for the voice/model combination,\n        or when the preset document has no ``prompt_audio_gcs_uri`` field.","operationId":"get_voice_prompt_audio_voices_prompt_audio__voice_id__get","parameters":[{"name":"voice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Voice Id"}},{"name":"model_version","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AIModelVersion","default":"async_flash_v1.0"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voices/{voice_id}":{"get":{"tags":["Synth Voices"],"summary":"Get Voice","description":"Retrieve a single voice by its UUID.\n\nResult is cached in Redis (TTL controlled by the ``VOICE_CACHE_TTL`` env var,\ndefault 72 h).\n\nArgs:\n    voice_id: UUID of the voice to retrieve.\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    token_service: Injected token validation service.\n    request: Raw FastAPI ``Request``.\n    version: API version extracted from the ``version`` request header.\n\nReturns:\n    VoiceResponse: Voice metadata.\n\nRaises:\n    VoiceNotFound: When the voice does not exist or is not accessible by the caller.","operationId":"get_voice_voices__voice_id__get","parameters":[{"name":"voice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Voice Id"}},{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Synth Voices"],"summary":"Update Voice","description":"Update mutable metadata fields of a voice.\n\nInvalidates the ``user_voice`` and ``user_voice_preview`` Redis cache entries for\nthe affected voice after a successful update.\n\nArgs:\n    voice_id: UUID of the voice to update.\n    payload: Fields to update (name, description, style, etc.).\n    token: Authenticated token with user context.\n    _request: Raw FastAPI ``Request``.\n    version: API version extracted from the ``version`` request header.\n    x_api_key: Bearer API key (used as a cache-key component for invalidation).\n\nReturns:\n    None: HTTP 204 No Content on success.","operationId":"update_voice_voices__voice_id__put","parameters":[{"name":"voice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Voice Id"}},{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceUpdateRequest"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Synth Voices"],"summary":"Delete Voice","description":"Delete a voice and its associated GCS assets.\n\nInvalidates the ``user_voice`` and ``user_voice_preview`` Redis cache entries for\nthe affected voice after successful deletion.\n\nArgs:\n    voice_id: UUID of the voice to delete.\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    token_service: Injected token validation service.\n    _request: Raw FastAPI ``Request``.\n    version: API version extracted from the ``version`` request header.\n\nReturns:\n    None: HTTP 204 No Content on success.","operationId":"delete_voice_voices__voice_id__delete","parameters":[{"name":"voice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Voice Id"}},{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voices/preview/{voice_id}":{"get":{"tags":["Synth Voices"],"summary":"Get Voice Preview","description":"Return a short-lived signed GCS URL for the voice preview audio.\n\nResult is cached in Redis (TTL controlled by the ``VOICE_PREVIEW_CACHE_TTL`` env var,\ndefault 10 min).\n\nArgs:\n    voice_id: UUID of the voice whose preview should be fetched.\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    token_service: Injected token validation service.\n    request: Raw FastAPI ``Request``.\n    version: API version extracted from the ``version`` request header.\n\nReturns:\n    VoicePreviewResponse: Signed URL pointing to the preview audio clip.","operationId":"get_voice_preview_voices_preview__voice_id__get","parameters":[{"name":"voice_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Voice Id"}},{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoicePreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voices":{"post":{"tags":["Synth Voices"],"summary":"List Voices","description":"List voices accessible to the authenticated token.\n\nSupports filtering and pagination via ``VoiceListRequest`` fields.\n\nArgs:\n    request: Optional filter/pagination parameters (defaults to an empty request).\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    token_service: Injected token validation service.\n    _request: Raw FastAPI ``Request``.\n    version: API version extracted from the ``version`` request header.\n\nReturns:\n    VoiceListResponse: Paginated list of voices with metadata.","operationId":"list_voices_voices_post","parameters":[{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceListRequest","default":{"limit":10}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voices/batch":{"post":{"tags":["Synth Voices"],"summary":"Get Voices Batch","description":"Retrieve multiple voices in a single request.\n\nArgs:\n    payload: List of voice UUIDs to fetch.\n    x_api_key: Bearer API key from the ``x-api-key`` header.\n    token_service: Injected token validation service.\n    request: Raw FastAPI ``Request``.\n    version: API version extracted from the ``version`` request header.\n\nReturns:\n    List[VoiceResponse]: Voice metadata for each requested UUID.","operationId":"get_voices_batch_voices_batch_post","parameters":[{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceBatchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VoiceResponse"},"title":"Response Get Voices Batch Voices Batch Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voices/clone":{"post":{"tags":["Synth Voices"],"summary":"Voice Clone","description":"Clone a voice from an uploaded audio prompt.\n\nRuns the clone pipeline concurrently for each requested ``ai_model_versions`` entry\nusing ``asyncio.TaskGroup``.  Partial failures are tolerated — a voice record is\npersisted in the database even if some model versions fail, as long as at least one\nsucceeds.  The original audio is uploaded to GCS in parallel with the clone jobs.\n\nSuper-user-only fields (``voice_id``, ``prompt_chunk_duration``, ``preset_quality``\n!= 3, ``voice_details_id``, and ``voice_type=PREDEFINED``) raise ``AccessDenied``\nfor non-super-user tokens.\n\nArgs:\n    audio: Prompt audio file (validated for format and size).\n    token: Authenticated token with user context.\n    version: API version extracted from the ``version`` request header.\n    name: Display name for the new voice.\n    description: Optional description.\n    enhance: Whether to apply audio enhancement before cloning.\n    transcript: Optional transcript of the prompt audio.\n    style: Voice style tag.\n    gender: Speaker gender.\n    accent: Speaker accent tag.\n    voice_type: ``CUSTOM`` (default) or ``PREDEFINED`` (super-user only).\n    preset_quality: Clone quality level (super-user only to change from default 3).\n    voice_details_id: Optional link to an existing voice details record (super-user only).\n    prompt_chunk_duration: Override for prompt chunk duration after preprocessing\n        (super-user only; deprecated in non-local/dev environments).\n    voice_id: Explicit UUID to assign to the new voice (super-user only).\n    ai_model_versions: Model versions to run the clone pipeline for.\n    ai_voice_service: Voice metadata service.\n    google_storage: GCS client for audio upload.\n    prompt_language: Force the language of the prompt audio instead of auto-detecting.\n\nReturns:\n    CloneVoiceResponse: ID, name, description, and detected language of the new voice.\n\nRaises:\n    AccessDenied: When a non-super-user supplies restricted fields.\n    VoiceCloneLimitExceeded: When the user has reached the voice-clone cap.\n    FileTooLargeException: When the uploaded audio exceeds the configured size limit.\n    Exception: Re-raises the primary model-version error when all clone jobs fail.","operationId":"voice_clone_voices_clone_post","parameters":[{"name":"voice_details_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Voice Details Id"}},{"name":"version","in":"header","required":false,"schema":{"$ref":"#/components/schemas/APIVersion","default":"v1"}},{"name":"X-Api-Key","in":"header","required":false,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_voice_clone_voices_clone_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloneVoiceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AIModelVersion":{"type":"string","enum":["async_flash_v1.0","async_flash_v1.5","async_pro_v1.0"],"title":"AIModelVersion"},"APIVersion":{"type":"string","enum":["v1"],"title":"APIVersion"},"Alignment":{"properties":{"words":{"items":{"type":"string"},"type":"array","title":"Words","description":"List of characters from the transcript"},"word_start_times_milliseconds":{"items":{"type":"number"},"type":"array","title":"Word Start Times Milliseconds","description":"Start time of each character in milliseconds"},"word_end_times_milliseconds":{"items":{"type":"number"},"type":"array","title":"Word End Times Milliseconds","description":"End time of each character in milliseconds"}},"type":"object","required":["words","word_start_times_milliseconds","word_end_times_milliseconds"],"title":"Alignment","description":"Represents the alignment data for text-to-speech with timestamps.\n\nThis model contains the timing information for each character in the generated speech,\nallowing applications to synchronize visual elements with the audio."},"Body_speech_to_text_speech_to_text__post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"Audio file (wav/mp3/flac/webm/...)"},"model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Id","description":"ASR model id; defaults to config."},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"Optional language hint."}},"type":"object","required":["file"],"title":"Body_speech_to_text_speech_to_text__post"},"Body_voice_clone_voices_clone_post":{"properties":{"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description","default":""},"enhance":{"type":"boolean","title":"Enhance","default":false},"transcript":{"type":"string","title":"Transcript","default":""},"style":{"type":"string","title":"Style","default":""},"gender":{"$ref":"#/components/schemas/Gender","default":"Unspecified"},"accent":{"type":"string","title":"Accent","default":""},"voice_type":{"$ref":"#/components/schemas/VoiceType","default":"CUSTOM"},"preset_quality":{"type":"integer","title":"Preset Quality","default":3},"prompt_chunk_duration":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Prompt Chunk Duration","description":"Duration of prompt audio after preprocessing."},"voice_id":{"anyOf":[{"type":"string"},{"type":"string","format":"uuid"},{"type":"null"}],"title":"Voice Id"},"ai_model_versions":{"items":{"$ref":"#/components/schemas/AIModelVersion"},"type":"array","title":"Ai Model Versions","default":["async_flash_v1.0","async_flash_v1.5","async_pro_v1.0"]},"prompt_language":{"anyOf":[{"$ref":"#/components/schemas/LanguageEnum"},{"type":"null"}],"description":"Force language of prompt audio."},"audio":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio"}},"type":"object","required":["name","audio"],"title":"Body_voice_clone_voices_clone_post"},"CloneVoiceResponse":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"string","format":"uuid"}],"title":"Id"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"language":{"type":"string","title":"Language"}},"type":"object","required":["id","name","description","language"],"title":"CloneVoiceResponse"},"DevToggles":{"properties":{"disable_token_validation":{"type":"boolean","title":"Disable Token Validation","default":false},"disable_postgresql":{"type":"boolean","title":"Disable Postgresql","default":false},"disable_mongodb":{"type":"boolean","title":"Disable Mongodb","default":false},"disable_rate_limiter":{"type":"boolean","title":"Disable Rate Limiter","default":false},"disable_concurrency_limiter":{"type":"boolean","title":"Disable Concurrency Limiter","default":false},"disable_pubsub":{"type":"boolean","title":"Disable Pubsub","default":false},"disable_language_detector":{"type":"boolean","title":"Disable Language Detector","default":false},"disable_sentence_splitter":{"type":"boolean","title":"Disable Sentence Splitter","default":false},"disable_normalizer":{"type":"boolean","title":"Disable Normalizer","default":false},"disable_phonemizer":{"type":"boolean","title":"Disable Phonemizer","default":false},"disable_stp":{"type":"boolean","title":"Disable Stp","default":false},"disable_xcodec":{"type":"boolean","title":"Disable Xcodec","default":false},"disable_transcript_validation":{"type":"boolean","title":"Disable Transcript Validation","default":false},"disable_stp_short_target_standalone":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Disable Stp Short Target Standalone"}},"type":"object","title":"DevToggles","description":"Hidden dev-only flags for disabling external services during concurrency profiling.\nAccepted under the ``__dev_toggles`` JSON key. Silently ignored outside ENVIRONMENT=dev."},"ErrorResponse":{"properties":{"error_code":{"type":"string","title":"Error Code","examples":["RATE_LIMIT_EXCEEDED"]},"message":{"type":"string","title":"Message","examples":["You have exceeded your rate limit."]},"extra":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"}]},"type":"object"},{"type":"null"}],"title":"Extra","examples":[{"limit":5}]}},"type":"object","required":["error_code","message"],"title":"ErrorResponse"},"Gender":{"type":"string","enum":["Male","Female","Neutral","Unspecified"],"title":"Gender"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LangDetectionMode":{"type":"string","enum":["llm","fasttext"],"title":"LangDetectionMode"},"LanguageEnum":{"type":"string","enum":["en","fr","it","de","es","pt","ar","ru","ro","ja","he","hy","tr","hi","zh","cmn","ur"],"title":"LanguageEnum"},"Mp3OutputFormat":{"properties":{"container":{"type":"string","const":"mp3","title":"Container"},"encoding":{"type":"string","enum":["pcm_f32le","pcm_s16le"],"title":"Encoding","default":"pcm_s16le"},"bit_rate":{"type":"integer","maximum":320000.0,"minimum":32000.0,"title":"Bit Rate","default":192000},"sample_rate":{"type":"integer","maximum":48000.0,"minimum":8000.0,"title":"Sample Rate"}},"type":"object","required":["container","sample_rate"],"title":"Mp3OutputFormat"},"RawOutputFormat":{"properties":{"container":{"type":"string","const":"raw","title":"Container"},"encoding":{"type":"string","enum":["pcm_f32le","pcm_s16le","pcm_mulaw"],"title":"Encoding","default":"pcm_s16le"},"sample_rate":{"type":"integer","maximum":48000.0,"minimum":8000.0,"title":"Sample Rate","description":"Sample rate in Hz (e.g., 44100)"},"bit_rate":{"anyOf":[{"type":"integer","maximum":320000.0,"minimum":16000.0},{"type":"null"}],"title":"Bit Rate","default":16000}},"type":"object","required":["container","sample_rate"],"title":"RawOutputFormat"},"SpeechToTextJSONRequest":{"properties":{"audio_base64":{"type":"string","title":"Audio Base64","description":"Base64-encoded audio (wav/mp3/flac/...)"},"model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Id","description":"ASR model id; defaults to the configured ASR model."},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"Optional language hint."}},"type":"object","required":["audio_base64"],"title":"SpeechToTextJSONRequest","description":"JSON body for ``POST /speech_to_text/`` (application/json variant)."},"SpeechToTextResponse":{"properties":{"text":{"type":"string","title":"Text","description":"Full transcript text"}},"type":"object","required":["text"],"title":"SpeechToTextResponse"},"SyncTTSWithSUAudioResponse":{"properties":{"signed_url":{"type":"string","title":"Signed Url","description":"Signed URL to access the generated audio file","examples":["https://storage.googleapis.com/bucket_name/audio_file.wav"]}},"type":"object","required":["signed_url"],"title":"SyncTTSWithSUAudioResponse","description":"Response model for the synchronous text-to-speech with signed url."},"TTSAudioResponse":{"properties":{"audio_base64":{"type":"string","title":"Audio Base64","description":"Base64-encoded audio data"},"alignment":{"$ref":"#/components/schemas/Alignment","description":"Alignment data for synchronizing text with audio"}},"type":"object","required":["audio_base64","alignment"],"title":"TTSAudioResponse","description":"Response model for the synchronous text-to-speech with timestamps endpoint.\n\nThis model contains the generated audio as a base64-encoded string and\nthe alignment data for synchronizing text with the audio."},"TTSRequest":{"properties":{"__dev_toggles":{"anyOf":[{"$ref":"#/components/schemas/DevToggles"},{"type":"null"}]},"model_id":{"$ref":"#/components/schemas/AIModelVersion","default":"async_flash_v1.0","examples":["async_flash_v1.0"]},"normalize":{"type":"boolean","title":"Normalize","description":"Whether to normalize the input text. If False, the input text will be used as is.","default":false},"experimental_streaming_mode":{"type":"boolean","title":"Experimental Streaming Mode","description":"Whether to stream audio in experimental mode.","default":false},"experimental_stp_v2":{"type":"boolean","title":"Experimental Stp V2","description":"Use v2 STP prompt format (pr_speech_start/end). Only has effect when experimental_streaming_mode=True.","default":false},"transcript":{"type":"string","minLength":1,"title":"Transcript","examples":["Hello, world!"]},"previous_request_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Previous Request Id","description":"ID returned in the X-Request-Id header of a previous TTS response. When provided, the last sentence of that request is appended AFTER the voice reference, enabling voice continuity across requests. Effective only for STP-based models (async_flash_v1.5, async_pro_v1.0). Silently falls back to the voice reference alone when the entry has expired, is missing, or belongs to a different token / voice / model / experimental_stp_v2 setting."},"language":{"anyOf":[{"$ref":"#/components/schemas/LanguageEnum"},{"type":"null"}],"description":"Target language.","examples":["en"]},"lang_detection_mode":{"$ref":"#/components/schemas/LangDetectionMode","description":"Language detection backend when `language` is not provided. 'llm' uses the normalizer model (default); 'fasttext' uses the local fastText model.","default":"llm"},"voice":{"$ref":"#/components/schemas/VoiceIdSpecifier"},"output_format":{"anyOf":[{"$ref":"#/components/schemas/RawOutputFormat"},{"$ref":"#/components/schemas/WavOutputFormat"},{"$ref":"#/components/schemas/Mp3OutputFormat"}],"title":"Output Format","description":"Default is raw / pcm_s16le / 44100 Hz","default":{"container":"raw","encoding":"pcm_s16le","sample_rate":44100,"bit_rate":192000}},"sentence_splitter_chunk_size":{"anyOf":[{"type":"integer","maximum":500.0,"minimum":1.0},{"type":"null"}],"title":"Sentence Splitter Chunk Size","description":"Number of characters to split the transcript into chunks. None if based on sentences."},"duration":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration"},"stability":{"type":"integer","maximum":100.0,"minimum":0.0,"title":"Stability","description":"TTS's stability.","default":50},"speed_control":{"type":"number","maximum":2.0,"minimum":0.7,"title":"Speed Control","description":"TTS's speed control.","default":1.0}},"type":"object","required":["transcript","voice"],"title":"TTSRequest"},"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"},"VoiceBatchRequest":{"properties":{"voice_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":100,"minItems":1,"title":"Voice Ids"}},"type":"object","required":["voice_ids"],"title":"VoiceBatchRequest"},"VoiceIdSpecifier":{"properties":{"mode":{"type":"string","const":"id","title":"Mode"},"id":{"type":"string","format":"uuid","title":"Id"},"__experimental_controls":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Experimental Controls"}},"type":"object","required":["mode","id"],"title":"VoiceIdSpecifier"},"VoiceListRequest":{"properties":{"limit":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":1.0},{"type":"null"}],"title":"Limit","default":10},"after":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"After"},"my_voice":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"My Voice"},"gender":{"anyOf":[{"$ref":"#/components/schemas/Gender"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"accent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accent"},"style":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Style"},"api_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Version"},"model_id":{"anyOf":[{"$ref":"#/components/schemas/AIModelVersion"},{"type":"null"}],"examples":["async_flash_v1.0"]},"voice_type":{"anyOf":[{"$ref":"#/components/schemas/VoiceType"},{"type":"null"}]},"min_quality":{"anyOf":[{"type":"integer","maximum":5.0,"minimum":0.0},{"type":"null"}],"title":"Min Quality"},"max_quality":{"anyOf":[{"type":"integer","maximum":5.0,"minimum":0.0},{"type":"null"}],"title":"Max Quality"},"name_contains":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name Contains"}},"type":"object","title":"VoiceListRequest"},"VoiceListResponse":{"properties":{"voices":{"items":{"$ref":"#/components/schemas/VoiceResponse"},"type":"array","title":"Voices"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["voices"],"title":"VoiceListResponse"},"VoicePreviewResponse":{"properties":{"signed_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signed Url"}},"type":"object","title":"VoicePreviewResponse"},"VoiceResponse":{"properties":{"voice_id":{"type":"string","format":"uuid","title":"Voice Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"gender":{"anyOf":[{"$ref":"#/components/schemas/Gender"},{"type":"null"}]},"accent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accent"},"style":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Style"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"voice_type":{"$ref":"#/components/schemas/VoiceType"}},"type":"object","required":["voice_id","name","description","language","gender","accent","style","created_at","updated_at","voice_type"],"title":"VoiceResponse"},"VoiceType":{"type":"string","enum":["PREDEFINED","CUSTOM"],"title":"VoiceType"},"VoiceUpdateRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":100,"minLength":0},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","minLength":0},{"type":"null"}],"title":"Description"},"gender":{"anyOf":[{"$ref":"#/components/schemas/Gender","minLength":0},{"type":"null"}]},"accent":{"anyOf":[{"type":"string","minLength":0},{"type":"null"}],"title":"Accent"},"style":{"anyOf":[{"type":"string","minLength":0},{"type":"null"}],"title":"Style"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"language":{"anyOf":[{"type":"string","minLength":0},{"type":"null"}],"title":"Language"}},"additionalProperties":false,"type":"object","title":"VoiceUpdateRequest"},"WavOutputFormat":{"properties":{"container":{"type":"string","const":"wav","title":"Container"},"encoding":{"type":"string","enum":["pcm_f32le","pcm_s16le"],"title":"Encoding","default":"pcm_s16le"},"sample_rate":{"type":"integer","maximum":48000.0,"minimum":8000.0,"title":"Sample Rate"},"bit_rate":{"anyOf":[{"type":"integer","maximum":320000.0,"minimum":16000.0},{"type":"null"}],"title":"Bit Rate"}},"type":"object","required":["container","sample_rate"],"title":"WavOutputFormat"}}}}