{
  "openapi": "3.1.0",
  "info": {
    "title": "Voicely Text-to-Speech API",
    "version": "beta",
    "description": "Public Beta. MP3 or raw audio (PCM, G.711 μ-law) synthesis, complete or streamed, for server-side integrations. Billing: each successful request is billed in 100-character blocks (rounded up); 1 paid Voicely credit = 1000 API characters. Streaming: /stream returns the same audio (MP3 or a raw format) while it is generated; a clean end of the body means completed and charged, an errored body means not charged. No WebSocket or voice cloning."
  },
  "servers": [
    {
      "url": "https://api.tryvoicely.com",
      "description": "Canonical API base"
    }
  ],
  "security": [
    {
      "xiApiKey": []
    },
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "xiApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "xi-api-key"
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "detail": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          },
          "code": {
            "type": "string",
            "enum": [
              "api_key_in_query",
              "client_aborted",
              "empty_text",
              "invalid_content_type",
              "invalid_idempotency_key",
              "invalid_json",
              "invalid_request",
              "invalid_text",
              "missing_text",
              "invalid_api_key",
              "missing_api_key",
              "api_spend_limit_reached",
              "insufficient_balance",
              "account_suspended",
              "revoked_api_key",
              "voice_not_found",
              "idempotency_mismatch",
              "replay_expired",
              "replay_unavailable",
              "request_in_progress",
              "body_too_large",
              "text_too_long",
              "invalid_apply_language_text_normalization",
              "invalid_apply_text_normalization",
              "invalid_context_text",
              "invalid_request_ids",
              "invalid_seed",
              "invalid_voice_settings",
              "provider_input_too_long",
              "provider_rejected_text",
              "provider_unsupported_voice",
              "text_not_speakable",
              "unsupported_language",
              "unsupported_model",
              "unsupported_output_format",
              "unsupported_pronunciation_dictionaries",
              "concurrency_limit_exceeded",
              "rate_limit_exceeded",
              "already_finalized",
              "artifact_conflict",
              "artifact_install_failed",
              "audio_conversion_failed",
              "audio_validation_failed",
              "capture_failed",
              "internal_error",
              "replay_store_failed",
              "request_interrupted",
              "stream_validation_failed",
              "provider_auth_error",
              "provider_billing_error",
              "provider_error",
              "provider_incomplete",
              "provider_invalid_audio",
              "provider_invalid_output",
              "provider_invalid_response",
              "provider_no_final_marker",
              "provider_permission_denied",
              "stream_interrupted",
              "api_disabled",
              "capture_failed",
              "provider_quota_exceeded",
              "provider_rate_limited",
              "provider_unavailable",
              "schema_not_ready",
              "service_busy",
              "streaming_not_available",
              "provider_timeout",
              "stream_lifetime_exceeded"
            ]
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "VerifiedLanguage": {
        "type": "object",
        "required": [
          "language",
          "model_id"
        ],
        "properties": {
          "language": {
            "type": "string",
            "enum": [
              "en",
              "ur",
              "hi",
              "ar",
              "bn",
              "nl",
              "fr",
              "de",
              "id",
              "it",
              "ja",
              "ko",
              "mr",
              "pl",
              "pt",
              "ro",
              "ru",
              "es",
              "ta",
              "te",
              "th",
              "tr",
              "uk",
              "vi",
              "zh",
              "pa",
              "gu",
              "kn",
              "ml",
              "he",
              "sw"
            ]
          },
          "model_id": {
            "type": "string"
          },
          "accent": {
            "type": [
              "string",
              "null"
            ]
          },
          "locale": {
            "type": [
              "string",
              "null"
            ]
          },
          "preview_url": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Voice": {
        "type": "object",
        "description": "ElevenLabs voice shape plus two Voicely fields (languages, available_for_models). verified_languages lists only languages proven in production for that voice; languages lists the languages it speaks. Urdu is spoken by vly_f08 (female) and vly_m05 (male); Urdu sent with any other voice_id is spoken by the one of the same gender, and X-Voicely-Voice-Used says which.",
        "required": [
          "voice_id",
          "name",
          "category",
          "labels",
          "verified_languages",
          "languages",
          "available_for_models"
        ],
        "properties": {
          "voice_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "premade"
            ]
          },
          "labels": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "properties": {
              "gender": {
                "type": "string",
                "enum": [
                  "female",
                  "male"
                ]
              }
            }
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "preview_url": {
            "type": "null"
          },
          "settings": {
            "type": "null"
          },
          "sharing": {
            "type": "null"
          },
          "samples": {
            "type": "null"
          },
          "fine_tuning": {
            "type": "null"
          },
          "collection_ids": {
            "type": "null"
          },
          "voice_verification": {
            "type": "null"
          },
          "high_quality_base_model_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "verified_languages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VerifiedLanguage"
            }
          },
          "is_owner": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "created_at_unix": {
            "type": "null"
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "language_id",
                "name"
              ],
              "properties": {
                "language_id": {
                  "type": "string",
                  "enum": [
                    "en",
                    "ur",
                    "hi",
                    "ar",
                    "bn",
                    "nl",
                    "fr",
                    "de",
                    "id",
                    "it",
                    "ja",
                    "ko",
                    "mr",
                    "pl",
                    "pt",
                    "ro",
                    "ru",
                    "es",
                    "ta",
                    "te",
                    "th",
                    "tr",
                    "uk",
                    "vi",
                    "zh",
                    "pa",
                    "gu",
                    "kn",
                    "ml",
                    "he",
                    "sw"
                  ]
                },
                "name": {
                  "type": "string"
                }
              }
            }
          },
          "available_for_models": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "VoicePage": {
        "type": "object",
        "required": [
          "voices",
          "has_more",
          "total_count",
          "next_page_token"
        ],
        "properties": {
          "voices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Voice"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "total_count": {
            "type": "integer"
          },
          "next_page_token": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass back unchanged, with the same filters and sort. null on the last page."
          }
        }
      },
      "TtsRequest": {
        "type": "object",
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "string",
            "description": "1-1000 Unicode code points."
          },
          "model_id": {
            "type": "string",
            "default": "voicely-flash-v1",
            "anyOf": [
              {
                "const": "voicely-flash-v1"
              },
              {
                "const": "eleven_v3",
                "deprecated": true,
                "description": "Deprecated legacy compatibility alias for voicely-flash-v1; responses carry a Deprecation header. Send voicely-flash-v1."
              }
            ]
          },
          "language_code": {
            "type": "string",
            "enum": [
              "en",
              "ur",
              "hi",
              "ar",
              "bn",
              "nl",
              "fr",
              "de",
              "id",
              "it",
              "ja",
              "ko",
              "mr",
              "pl",
              "pt",
              "ro",
              "ru",
              "es",
              "ta",
              "te",
              "th",
              "tr",
              "uk",
              "vi",
              "zh",
              "pa",
              "gu",
              "kn",
              "ml",
              "he",
              "sw",
              "cmn",
              "ar-EG",
              "bn-BD",
              "nl-NL",
              "en-IN",
              "en-US",
              "fr-FR",
              "de-DE",
              "hi-IN",
              "id-ID",
              "it-IT",
              "ja-JP",
              "ko-KR",
              "mr-IN",
              "pl-PL",
              "pt-BR",
              "ro-RO",
              "ru-RU",
              "es-ES",
              "ta-IN",
              "te-IN",
              "th-TH",
              "tr-TR",
              "uk-UA",
              "ur-PK",
              "vi-VN",
              "en-GB",
              "en-AU",
              "es-MX",
              "es-419",
              "fr-CA",
              "pt-PT",
              "cmn-CN",
              "cmn-TW",
              "pa-IN",
              "gu-IN",
              "kn-IN",
              "ml-IN",
              "he-IL",
              "sw-KE",
              "zh-CN",
              "zh-TW"
            ],
            "description": "Every Voicely Studio language: an ISO code or an exact Studio locale. When omitted, only Urdu script, Devanagari and Latin text are detected (ur, hi, en)."
          }
        },
        "additionalProperties": true
      }
    }
  },
  "paths": {
    "/v1/models": {
      "get": {
        "summary": "List models",
        "responses": {
          "200": {
            "description": "Models"
          },
          "400": {
            "description": "api_key_in_query: Revoke that key (URLs end up in logs) and send the new one in the xi-api-key header. | client_aborted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | empty_text: Fix the request; retrying it unchanged will fail again. | invalid_content_type: Send Content-Type: application/json. | invalid_idempotency_key: Fix the request; retrying it unchanged will fail again. | invalid_json: Fix the request; retrying it unchanged will fail again. | invalid_request: Fix the request; retrying it unchanged will fail again. | invalid_text: Fix the request; retrying it unchanged will fail again. | missing_text: Fix the request; retrying it unchanged will fail again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key: Check the key; create a new one on the API access page if needed. | missing_api_key: Send your key in the xi-api-key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended: Contact support (tryvoicely.com/contact). | revoked_api_key: Create a new key on the API access page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "already_finalized: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_conversion_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | internal_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | replay_store_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | request_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "api_disabled: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_quota_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_rate_limited: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_unavailable: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | schema_not_ready: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | service_busy: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/voices": {
      "get": {
        "summary": "List voices",
        "responses": {
          "200": {
            "description": "Voices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "voices"
                  ],
                  "properties": {
                    "voices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Voice"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "api_key_in_query: Revoke that key (URLs end up in logs) and send the new one in the xi-api-key header. | client_aborted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | empty_text: Fix the request; retrying it unchanged will fail again. | invalid_content_type: Send Content-Type: application/json. | invalid_idempotency_key: Fix the request; retrying it unchanged will fail again. | invalid_json: Fix the request; retrying it unchanged will fail again. | invalid_request: Fix the request; retrying it unchanged will fail again. | invalid_text: Fix the request; retrying it unchanged will fail again. | missing_text: Fix the request; retrying it unchanged will fail again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key: Check the key; create a new one on the API access page if needed. | missing_api_key: Send your key in the xi-api-key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended: Contact support (tryvoicely.com/contact). | revoked_api_key: Create a new key on the API access page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "already_finalized: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_conversion_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | internal_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | replay_store_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | request_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "api_disabled: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_quota_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_rate_limited: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_unavailable: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | schema_not_ready: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | service_busy: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v2/voices": {
      "get": {
        "summary": "Search and page voices (ElevenLabs v2 shape)",
        "description": "Returns a VoicePage. List parameters (language, use_cases, voice_ids) may be repeated (what the official SDKs send), comma-separated or a JSON array string. Filters no Voicely voice can satisfy (age, accent, use_cases, collection_id, fine_tuning_state, high_quality=true, a category other than premade, a voice_type other than default) return no voices. min_notice_period_days, include_custom_rates, include_live_moderated and unknown parameters change nothing and are named in X-Voicely-Ignored-Parameters.",
        "parameters": [
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            }
          },
          {
            "name": "next_page_token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "From the previous page; only valid with the same filters and sort."
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Name, description, labels and category."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "created_at_unix"
              ]
            }
          },
          {
            "name": "sort_direction",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          },
          {
            "name": "voice_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "personal",
                "community",
                "default",
                "workspace",
                "non-default",
                "non-community",
                "saved"
              ]
            },
            "description": "default returns every voice; the others return none."
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "premade",
                "cloned",
                "generated",
                "professional"
              ]
            },
            "description": "premade returns every voice; the others return none."
          },
          {
            "name": "gender",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "female",
                "male"
              ]
            }
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "description": "Any Voicely Studio language code (locales such as hi-IN or es-MX accepted): the voices that speak it. Urdu returns the two Urdu voices (one female, one male)."
          },
          {
            "name": "voice_ids",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "maxItems": 100
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "use_cases",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "description": "No Voicely voice has a use_case label: any value returns none."
          },
          {
            "name": "age",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "No Voicely voice has an age label: any value returns none."
          },
          {
            "name": "accent",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "No Voicely voice has an accent label: any value returns none."
          },
          {
            "name": "collection_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Any value returns none."
          },
          {
            "name": "fine_tuning_state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "not_verified",
                "not_started",
                "queued",
                "fine_tuning",
                "fine_tuned",
                "failed",
                "delayed"
              ]
            },
            "description": "Any value returns none (professional clones only)."
          },
          {
            "name": "min_notice_period_days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100000
            },
            "description": "Validated; changes nothing (no Voicely voice is shared). Named in X-Voicely-Ignored-Parameters."
          },
          {
            "name": "include_custom_rates",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Validated; changes nothing. Named in X-Voicely-Ignored-Parameters."
          },
          {
            "name": "include_live_moderated",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Validated; changes nothing. Named in X-Voicely-Ignored-Parameters."
          },
          {
            "name": "high_quality",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "true returns none."
          },
          {
            "name": "include_total_count",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "total_count is always returned."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of voices",
            "headers": {
              "X-Voicely-Ignored-Parameters": {
                "schema": {
                  "type": "string"
                },
                "description": "Comma-separated query.<name> list of parameters that changed nothing: at most 30 names and 1,024 bytes, then more=<n>. Absent when none."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoicePage"
                }
              }
            }
          },
          "400": {
            "description": "api_key_in_query: Revoke that key (URLs end up in logs) and send the new one in the xi-api-key header. | client_aborted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | empty_text: Fix the request; retrying it unchanged will fail again. | invalid_content_type: Send Content-Type: application/json. | invalid_idempotency_key: Fix the request; retrying it unchanged will fail again. | invalid_json: Fix the request; retrying it unchanged will fail again. | invalid_request: Fix the request; retrying it unchanged will fail again. | invalid_text: Fix the request; retrying it unchanged will fail again. | missing_text: Fix the request; retrying it unchanged will fail again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key: Check the key; create a new one on the API access page if needed. | missing_api_key: Send your key in the xi-api-key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended: Contact support (tryvoicely.com/contact). | revoked_api_key: Create a new key on the API access page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "already_finalized: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_conversion_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | internal_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | replay_store_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | request_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "api_disabled: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_quota_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_rate_limited: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_unavailable: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | schema_not_ready: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | service_busy: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/voices/{voice_id}": {
      "get": {
        "summary": "Get one voice",
        "parameters": [
          {
            "name": "voice_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "vly_f01",
                "vly_f02",
                "vly_f03",
                "vly_f04",
                "vly_f05",
                "vly_f06",
                "vly_f07",
                "vly_f08",
                "vly_f09",
                "vly_f10",
                "vly_f11",
                "vly_f12",
                "vly_f13",
                "vly_f14",
                "vly_m01",
                "vly_m02",
                "vly_m03",
                "vly_m04",
                "vly_m05",
                "vly_m06",
                "vly_m07",
                "vly_m08",
                "vly_m09",
                "vly_m10",
                "vly_m11",
                "vly_m12",
                "vly_m13",
                "vly_m14",
                "vly_m15",
                "vly_m16"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Voice",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Voice"
                }
              }
            }
          },
          "400": {
            "description": "api_key_in_query: Revoke that key (URLs end up in logs) and send the new one in the xi-api-key header. | client_aborted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | empty_text: Fix the request; retrying it unchanged will fail again. | invalid_content_type: Send Content-Type: application/json. | invalid_idempotency_key: Fix the request; retrying it unchanged will fail again. | invalid_json: Fix the request; retrying it unchanged will fail again. | invalid_request: Fix the request; retrying it unchanged will fail again. | invalid_text: Fix the request; retrying it unchanged will fail again. | missing_text: Fix the request; retrying it unchanged will fail again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key: Check the key; create a new one on the API access page if needed. | missing_api_key: Send your key in the xi-api-key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended: Contact support (tryvoicely.com/contact). | revoked_api_key: Create a new key on the API access page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "voice_not_found: Use an id from GET /v2/voices or GET /v1/voices.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "already_finalized: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_conversion_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | internal_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | replay_store_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | request_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "api_disabled: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_quota_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_rate_limited: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_unavailable: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | schema_not_ready: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | service_busy: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/user": {
      "get": {
        "summary": "Account, key and balance summary",
        "responses": {
          "200": {
            "description": "User"
          },
          "400": {
            "description": "api_key_in_query: Revoke that key (URLs end up in logs) and send the new one in the xi-api-key header. | client_aborted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | empty_text: Fix the request; retrying it unchanged will fail again. | invalid_content_type: Send Content-Type: application/json. | invalid_idempotency_key: Fix the request; retrying it unchanged will fail again. | invalid_json: Fix the request; retrying it unchanged will fail again. | invalid_request: Fix the request; retrying it unchanged will fail again. | invalid_text: Fix the request; retrying it unchanged will fail again. | missing_text: Fix the request; retrying it unchanged will fail again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key: Check the key; create a new one on the API access page if needed. | missing_api_key: Send your key in the xi-api-key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended: Contact support (tryvoicely.com/contact). | revoked_api_key: Create a new key on the API access page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "already_finalized: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_conversion_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | internal_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | replay_store_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | request_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "api_disabled: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_quota_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_rate_limited: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_unavailable: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | schema_not_ready: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | service_busy: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/user/subscription": {
      "get": {
        "summary": "Balances, limits and conversion setting",
        "responses": {
          "200": {
            "description": "Subscription"
          },
          "400": {
            "description": "api_key_in_query: Revoke that key (URLs end up in logs) and send the new one in the xi-api-key header. | client_aborted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | empty_text: Fix the request; retrying it unchanged will fail again. | invalid_content_type: Send Content-Type: application/json. | invalid_idempotency_key: Fix the request; retrying it unchanged will fail again. | invalid_json: Fix the request; retrying it unchanged will fail again. | invalid_request: Fix the request; retrying it unchanged will fail again. | invalid_text: Fix the request; retrying it unchanged will fail again. | missing_text: Fix the request; retrying it unchanged will fail again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key: Check the key; create a new one on the API access page if needed. | missing_api_key: Send your key in the xi-api-key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended: Contact support (tryvoicely.com/contact). | revoked_api_key: Create a new key on the API access page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "already_finalized: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_conversion_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | internal_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | replay_store_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | request_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "api_disabled: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_quota_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_rate_limited: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_unavailable: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | schema_not_ready: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | service_busy: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/text-to-speech/{voice_id}": {
      "post": {
        "summary": "Synthesize speech (complete MP3 or raw audio)",
        "parameters": [
          {
            "name": "voice_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "vly_f01",
                "vly_f02",
                "vly_f03",
                "vly_f04",
                "vly_f05",
                "vly_f06",
                "vly_f07",
                "vly_f08",
                "vly_f09",
                "vly_f10",
                "vly_f11",
                "vly_f12",
                "vly_f13",
                "vly_f14",
                "vly_m01",
                "vly_m02",
                "vly_m03",
                "vly_m04",
                "vly_m05",
                "vly_m06",
                "vly_m07",
                "vly_m08",
                "vly_m09",
                "vly_m10",
                "vly_m11",
                "vly_m12",
                "vly_m13",
                "vly_m14",
                "vly_m15",
                "vly_m16"
              ]
            }
          },
          {
            "name": "output_format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "mp3_44100_128",
                "pcm_24000",
                "pcm_16000",
                "ulaw_8000"
              ],
              "default": "mp3_44100_128"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[\\x21-\\x7E]{1,128}$"
            },
            "description": "Safe retries: the same key and payload return the stored audio for 48 h without a second charge."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TtsRequest"
              },
              "example": {
                "text": "Hello from the Voicely API.",
                "model_id": "voicely-flash-v1",
                "language_code": "en"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The complete audio in the requested output_format (MP3 by default).",
            "headers": {
              "X-Voicely-Characters-Input": {
                "schema": {
                  "type": "integer"
                },
                "description": "Actual characters in the request."
              },
              "X-Voicely-Characters-Charged": {
                "schema": {
                  "type": "integer"
                },
                "description": "Billed characters (100-char blocks)."
              },
              "X-Voicely-Character-Balance": {
                "schema": {
                  "type": "integer"
                },
                "description": "Spendable characters left."
              },
              "X-Voicely-Request-Id": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "audio/mpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/pcm": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "pcm_24000: raw PCM, signed 16-bit little-endian, mono, 24,000 Hz, with no header, not a WAV file (audio/pcm). The engine’s own audio passed through unchanged (lossless); it skips the MP3 encoding step. About 384 kbit/s. pcm_16000: raw PCM, signed 16-bit little-endian, mono, 16,000 Hz, with no header (audio/pcm). Resampled from the engine’s 24 kHz audio. About 256 kbit/s."
                }
              },
              "audio/basic": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "ulaw_8000: raw G.711 μ-law, mono, 8,000 Hz, 1 byte per sample, with no header, not an AU file (audio/basic). Phone-line (narrowband) quality, for telephony. 64 kbit/s."
                }
              }
            }
          },
          "400": {
            "description": "api_key_in_query: Revoke that key (URLs end up in logs) and send the new one in the xi-api-key header. | client_aborted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | empty_text: Fix the request; retrying it unchanged will fail again. | invalid_content_type: Send Content-Type: application/json. | invalid_idempotency_key: Fix the request; retrying it unchanged will fail again. | invalid_json: Fix the request; retrying it unchanged will fail again. | invalid_request: Fix the request; retrying it unchanged will fail again. | invalid_text: Fix the request; retrying it unchanged will fail again. | missing_text: Fix the request; retrying it unchanged will fail again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key: Check the key; create a new one on the API access page if needed. | missing_api_key: Send your key in the xi-api-key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "api_spend_limit_reached: Wait for X-Voicely-Spend-Limit-Reset (00:00 UTC) or raise your daily limit, then retry. | insufficient_balance: Buy credits or turn on auto-conversion, then retry (the same Idempotency-Key may be reused).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended: Contact support (tryvoicely.com/contact). | revoked_api_key: Create a new key on the API access page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "voice_not_found: Use an id from GET /v2/voices or GET /v1/voices.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "idempotency_mismatch: Use a new Idempotency-Key for a different request. | replay_expired: Send a new request with a new Idempotency-Key (it will be charged). | replay_unavailable: Send a new request with a new Idempotency-Key. | request_in_progress: Retry the same request with the same Idempotency-Key after Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large: Fix the request; retrying it unchanged will fail again. | text_too_long: Split the text into shorter requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "invalid_apply_language_text_normalization: Fix the request; retrying it unchanged will fail again. | invalid_apply_text_normalization: Fix the request; retrying it unchanged will fail again. | invalid_context_text: Fix the request; retrying it unchanged will fail again. | invalid_request_ids: Fix the request; retrying it unchanged will fail again. | invalid_seed: Fix the request; retrying it unchanged will fail again. | invalid_voice_settings: Fix the request; retrying it unchanged will fail again. | provider_input_too_long: Shorten the text. You were not charged. | provider_rejected_text: Change the text; retrying it unchanged will fail again. You were not charged. | provider_unsupported_voice: Choose another voice or language_code. You were not charged. | text_not_speakable: Fix the request; retrying it unchanged will fail again. | unsupported_language: Fix the request; retrying it unchanged will fail again. | unsupported_model: Fix the request; retrying it unchanged will fail again. | unsupported_output_format: Fix the request; retrying it unchanged will fail again. | unsupported_pronunciation_dictionaries: Remove pronunciation_dictionary_locators.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "concurrency_limit_exceeded: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused. | rate_limit_exceeded: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "already_finalized: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | artifact_conflict (only from /stream, or a retry of an Idempotency-Key first used there): You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | artifact_install_failed (only from /stream, or a retry of an Idempotency-Key first used there): You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_conversion_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | internal_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | replay_store_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | request_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | stream_validation_failed (only from /stream, or a retry of an Idempotency-Key first used there): You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "provider_auth_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_billing_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_incomplete (only from /stream, or a retry of an Idempotency-Key first used there): You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_invalid_audio: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_invalid_output: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_invalid_response: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_no_final_marker (only from /stream, or a retry of an Idempotency-Key first used there): You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_permission_denied: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | stream_interrupted (only from /stream, or a retry of an Idempotency-Key first used there): You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "api_disabled: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_quota_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_rate_limited: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_unavailable: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | schema_not_ready: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | service_busy: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused. | streaming_not_available (only from /stream, or a retry of an Idempotency-Key first used there): You were not charged. Use POST /v1/text-to-speech/{voice_id} (buffered) for this request, and try /stream again later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "provider_timeout: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | stream_lifetime_exceeded (only from /stream, or a retry of an Idempotency-Key first used there): You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/text-to-speech/{voice_id}/stream": {
      "post": {
        "summary": "Synthesize speech (MP3 or raw audio, streamed while it is generated)",
        "description": "Same body, parameters, voices and authentication as the buffered endpoint. Audio bytes arrive while they are generated. A body that ends cleanly means the request completed and was charged; a body that ends with an error (reset, truncation) was not charged, so discard that audio and retry with a new Idempotency-Key. Before the first byte, errors are normal JSON responses. A stream lasts at most 120 s; audio is capped at 170 s. 503 streaming_not_available means streaming is paused or at capacity: use the buffered endpoint.",
        "parameters": [
          {
            "name": "voice_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "vly_f01",
                "vly_f02",
                "vly_f03",
                "vly_f04",
                "vly_f05",
                "vly_f06",
                "vly_f07",
                "vly_f08",
                "vly_f09",
                "vly_f10",
                "vly_f11",
                "vly_f12",
                "vly_f13",
                "vly_f14",
                "vly_m01",
                "vly_m02",
                "vly_m03",
                "vly_m04",
                "vly_m05",
                "vly_m06",
                "vly_m07",
                "vly_m08",
                "vly_m09",
                "vly_m10",
                "vly_m11",
                "vly_m12",
                "vly_m13",
                "vly_m14",
                "vly_m15",
                "vly_m16"
              ]
            }
          },
          {
            "name": "output_format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "mp3_44100_128",
                "pcm_24000",
                "pcm_16000",
                "ulaw_8000"
              ],
              "default": "mp3_44100_128"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[\\x21-\\x7E]{1,128}$"
            },
            "description": "Lets you confirm the outcome: the same key and payload return the stored audio in the same output_format (not streamed) after a clean end, or the stored error after a failed stream, for 48 h. Without a key no audio is stored."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TtsRequest"
              },
              "example": {
                "text": "Hello from the Voicely API.",
                "model_id": "voicely-flash-v1",
                "language_code": "en"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The audio in the requested output_format (MP3 by default), streamed. The charge is final only at a clean end of the body.",
            "headers": {
              "X-Voicely-Characters-Input": {
                "schema": {
                  "type": "integer"
                },
                "description": "Actual characters in the request."
              },
              "X-Voicely-Characters-Reserved": {
                "schema": {
                  "type": "integer"
                },
                "description": "Characters held for this request (100-char blocks); charged only at a clean end."
              },
              "X-Voicely-Charge-Status": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "reserved"
                  ]
                },
                "description": "Always reserved on a live stream: nothing is charged when headers are sent."
              },
              "X-Voicely-Request-Id": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "audio/mpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/pcm": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "pcm_24000: raw PCM, signed 16-bit little-endian, mono, 24,000 Hz, with no header, not a WAV file (audio/pcm). The engine’s own audio passed through unchanged (lossless); it skips the MP3 encoding step. About 384 kbit/s. pcm_16000: raw PCM, signed 16-bit little-endian, mono, 16,000 Hz, with no header (audio/pcm). Resampled from the engine’s 24 kHz audio. About 256 kbit/s."
                }
              },
              "audio/basic": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "ulaw_8000: raw G.711 μ-law, mono, 8,000 Hz, 1 byte per sample, with no header, not an AU file (audio/basic). Phone-line (narrowband) quality, for telephony. 64 kbit/s."
                }
              }
            }
          },
          "400": {
            "description": "api_key_in_query: Revoke that key (URLs end up in logs) and send the new one in the xi-api-key header. | client_aborted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | empty_text: Fix the request; retrying it unchanged will fail again. | invalid_content_type: Send Content-Type: application/json. | invalid_idempotency_key: Fix the request; retrying it unchanged will fail again. | invalid_json: Fix the request; retrying it unchanged will fail again. | invalid_request: Fix the request; retrying it unchanged will fail again. | invalid_text: Fix the request; retrying it unchanged will fail again. | missing_text: Fix the request; retrying it unchanged will fail again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "invalid_api_key: Check the key; create a new one on the API access page if needed. | missing_api_key: Send your key in the xi-api-key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "api_spend_limit_reached: Wait for X-Voicely-Spend-Limit-Reset (00:00 UTC) or raise your daily limit, then retry. | insufficient_balance: Buy credits or turn on auto-conversion, then retry (the same Idempotency-Key may be reused).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "account_suspended: Contact support (tryvoicely.com/contact). | revoked_api_key: Create a new key on the API access page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "voice_not_found: Use an id from GET /v2/voices or GET /v1/voices.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "idempotency_mismatch: Use a new Idempotency-Key for a different request. | replay_expired: Send a new request with a new Idempotency-Key (it will be charged). | replay_unavailable: Send a new request with a new Idempotency-Key. | request_in_progress: Retry the same request with the same Idempotency-Key after Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "body_too_large: Fix the request; retrying it unchanged will fail again. | text_too_long: Split the text into shorter requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "invalid_apply_language_text_normalization: Fix the request; retrying it unchanged will fail again. | invalid_apply_text_normalization: Fix the request; retrying it unchanged will fail again. | invalid_context_text: Fix the request; retrying it unchanged will fail again. | invalid_request_ids: Fix the request; retrying it unchanged will fail again. | invalid_seed: Fix the request; retrying it unchanged will fail again. | invalid_voice_settings: Fix the request; retrying it unchanged will fail again. | provider_input_too_long: Shorten the text. You were not charged. | provider_rejected_text: Change the text; retrying it unchanged will fail again. You were not charged. | provider_unsupported_voice: Choose another voice or language_code. You were not charged. | text_not_speakable: Fix the request; retrying it unchanged will fail again. | unsupported_language: Fix the request; retrying it unchanged will fail again. | unsupported_model: Fix the request; retrying it unchanged will fail again. | unsupported_output_format: Fix the request; retrying it unchanged will fail again. | unsupported_pronunciation_dictionaries: Remove pronunciation_dictionary_locators.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "concurrency_limit_exceeded: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused. | rate_limit_exceeded: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "already_finalized: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | artifact_conflict: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | artifact_install_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_conversion_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | audio_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | internal_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | replay_store_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | request_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | stream_validation_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "provider_auth_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_billing_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_error: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_incomplete: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_invalid_audio: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_invalid_output: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_invalid_response: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_no_final_marker: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_permission_denied: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | stream_interrupted: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "api_disabled: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | capture_failed: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_quota_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_rate_limited: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | provider_unavailable: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | schema_not_ready: You were not charged. Retry later with exponential backoff; the same Idempotency-Key may be reused. | service_busy: You were not charged. Retry after Retry-After; the same Idempotency-Key may be reused. | streaming_not_available: You were not charged. Use POST /v1/text-to-speech/{voice_id} (buffered) for this request, and try /stream again later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "provider_timeout: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error). | stream_lifetime_exceeded: You were not charged. Retry after a short backoff with a NEW Idempotency-Key (the old key replays this error).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}