{
  "openapi": "3.0.3",
  "info": {
    "title": "SecretVoice API",
    "description": "Expressive, low-latency text-to-speech. 29 voices, 21 languages, synchronous / streaming / realtime WebSocket delivery. Drop-in compatible with the ElevenLabs protocol (see x-compat annotations).",
    "version": "1.0.0"
  },
  "servers": [
    { "url": "https://api.secretvoice.dev", "description": "Production" }
  ],
  "security": [{ "ApiKeyAuth": [] }],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "xi-api-key",
        "description": "SecretVoice API key. Accepts xi-api-key (ElevenLabs clients), X-API-Key, or Authorization: Bearer."
      }
    },
    "headers": {
      "XRequestId": {
        "description": "Trace id for the request.",
        "schema": { "type": "string", "example": "req_01HZxK..." }
      },
      "XDuration": {
        "description": "Exact audio-seconds produced (the billed amount).",
        "schema": { "type": "number", "example": 8.3 }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["detail"],
        "properties": { "detail": { "type": "string" } }
      },
      "Voice": {
        "type": "object",
        "required": ["voice_id", "name", "category", "labels", "preview_url"],
        "properties": {
          "voice_id": { "type": "string", "example": "velvet" },
          "name": { "type": "string", "example": "Velvet" },
          "category": { "type": "string", "example": "premade" },
          "labels": { "type": "object", "required": ["gender"], "properties": { "gender": { "type": "string", "enum": ["female", "male"] }, "style": { "type": "string" } } },
          "preview_url": { "type": "string", "example": "https://api.secretvoice.dev/v1/voices/velvet/preview.mp3" }
        }
      },
      "VoiceSettings": {
        "type": "object",
        "description": "Voice overrides. Applied per request or stored per account (PATCH settings).",
        "properties": {
          "stability": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.5, "x-compat": "mapped", "x-compat_note": "remapped to engine temperature" },
          "similarity_boost": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.75, "x-compat": "mapped", "x-compat_note": "remapped to engine top_p" },
          "style": { "type": "number", "minimum": 0, "maximum": 1, "default": 0, "x-compat": "ignored", "x-compat_note": "accepted, no-op; signature style always present" },
          "speed": { "type": "number", "minimum": 0.5, "maximum": 2, "default": 1, "x-compat": "native" },
          "use_speaker_boost": { "type": "boolean", "default": true, "x-compat": "ignored" }
        }
      },
      "Model": {
        "type": "object",
        "required": ["model_id", "name", "can_do_text_to_speech", "can_do_voice_conversion"],
        "properties": {
          "model_id": { "type": "string", "example": "secret-v1" },
          "name": { "type": "string", "example": "SecretVoice v1" },
          "can_do_text_to_speech": { "type": "boolean", "example": true },
          "can_do_voice_conversion": { "type": "boolean", "example": false },
          "languages": { "type": "array", "items": { "type": "string" } }
        }
      },
      "TtsBody": {
        "type": "object",
        "required": ["text"],
        "properties": {
          "text": { "type": "string", "minLength": 1, "maxLength": 5000, "description": "Text to synthesize." },
          "model_id": { "type": "string", "default": "secret-v1", "description": "Model id. ElevenLabs model aliases are accepted and mapped." },
          "language_code": { "type": "string", "description": "ISO 639-1 code (21 supported). Default en." },
          "voice_settings": { "$ref": "#/components/schemas/VoiceSettings" },
          "previous_text": { "type": "string", "x-compat": "mapped", "x-compat_note": "continuity via previous-chunk conditioning" },
          "next_text": { "type": "string", "x-compat": "ignored" },
          "seed": { "type": "integer", "minimum": 0, "maximum": 4294967295, "x-compat": "ignored", "x-compat_note": "accepted, best-effort determinism" },
          "previous_request_ids": { "type": "array", "items": { "type": "string" }, "maxItems": 3, "x-compat": "ignored" },
          "next_request_ids": { "type": "array", "items": { "type": "string" }, "maxItems": 3, "x-compat": "ignored" },
          "pronunciation_dictionary_locators": { "type": "array", "items": { "type": "object" }, "x-compat": "ignored", "x-compat_note": "reserved" },
          "apply_text_normalization": { "type": "string", "enum": ["auto", "on", "off"], "default": "auto" }
        }
      },
      "HistoryItem": {
        "type": "object",
        "required": ["history_item_id", "voice_id", "text", "date_unix", "seconds"],
        "properties": {
          "history_item_id": { "type": "string", "example": "req_01HZxK..." },
          "voice_id": { "type": "string", "example": "velvet" },
          "text": { "type": "string" },
          "date_unix": { "type": "integer" },
          "seconds": { "type": "number", "description": "Audio-seconds produced" }
        }
      },
      "User": {
        "type": "object",
        "required": ["id", "email", "is_new_user"],
        "properties": {
          "id": { "type": "string", "example": "usr_01HZxK..." },
          "email": { "type": "string", "format": "email" },
          "is_new_user": { "type": "boolean" },
          "subscription": { "type": "object", "properties": { "tier": { "type": "string", "example": "pay_as_you_go" } } }
        }
      },
      "Subscription": {
        "type": "object",
        "required": ["tier", "character_count", "character_limit", "next_character_count_reset_unix"],
        "description": "ElevenLabs-shaped subscription. character_count is expressed in audio-seconds used.",
        "properties": {
          "tier": { "type": "string", "example": "pay_as_you_go" },
          "character_count": { "type": "number", "description": "Audio-seconds used", "example": 4123 },
          "character_limit": { "type": "number", "description": "-1 = unlimited", "example": -1 },
          "next_character_count_reset_unix": { "type": "integer", "example": 0 }
        }
      }
    },
    "responses": {
      "Unauthorized": { "description": "Missing or invalid API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "InsufficientBalance": { "description": "Insufficient balance", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "Rate limit exceeded", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "Resource not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Validation": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "InternalError": { "description": "Internal error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    }
  },
  "paths": {
    "/v1/models": {
      "get": {
        "summary": "List models",
        "responses": {
          "200": {
            "description": "Available models",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "models": { "type": "array", "items": { "$ref": "#/components/schemas/Model" } } } } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/voices": {
      "get": {
        "summary": "List voices",
        "responses": {
          "200": {
            "description": "Voice roster",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "voices": { "type": "array", "items": { "$ref": "#/components/schemas/Voice" } } } } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/voices/{voice_id}": {
      "parameters": [{ "in": "path", "name": "voice_id", "required": true, "schema": { "type": "string" } }],
      "get": {
        "summary": "Get a voice",
        "responses": {
          "200": { "description": "Voice", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Voice" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/voices/{voice_id}/preview.mp3": {
      "parameters": [{ "in": "path", "name": "voice_id", "required": true, "schema": { "type": "string" } }],
      "get": {
        "summary": "Voice preview audio",
        "description": "301 redirect to the voice's preview audio file. Used by the preview_url field in the voices response.",
        "responses": {
          "301": { "description": "Redirect to the preview audio" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/voices/{voice_id}/settings": {
      "parameters": [{ "in": "path", "name": "voice_id", "required": true, "schema": { "type": "string" } }],
      "get": {
        "summary": "Get voice settings (account override)",
        "responses": {
          "200": { "description": "Current settings", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VoiceSettings" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "patch": {
        "summary": "Update voice settings (account override)",
        "description": "Partial bodies accepted; omitted fields keep current values.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VoiceSettings" } } } },
        "responses": {
          "200": { "description": "Updated settings", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VoiceSettings" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/Validation" }
        }
      }
    },
    "/v1/text-to-speech/{voice_id}": {
      "parameters": [
        { "in": "path", "name": "voice_id", "required": true, "schema": { "type": "string" }, "description": "Roster voice shorthand (e.g. velvet)." },
        { "in": "query", "name": "output_format", "required": false, "schema": { "type": "string", "default": "mp3_44100_128", "description": "codec_sample_rate_bitrate: mp3_*, wav_*, pcm_*, opus_48000_*, ulaw_8000, alaw_8000." } },
        { "in": "query", "name": "enable_logging", "required": false, "schema": { "type": "boolean", "default": true }, "description": "false = zero retention, skip history." }
      ],
      "post": {
        "summary": "Convert text to speech",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TtsBody" } } } },
        "responses": {
          "200": {
            "description": "Generated audio file",
            "headers": {
              "X-Request-Id": { "$ref": "#/components/headers/XRequestId" },
              "X-Duration": { "$ref": "#/components/headers/XDuration" }
            },
            "content": {
              "audio/mpeg": { "schema": { "type": "string", "format": "binary" } },
              "audio/wav": { "schema": { "type": "string", "format": "binary" } },
              "audio/pcm": { "schema": { "type": "string", "format": "binary" } },
              "audio/opus": { "schema": { "type": "string", "format": "binary" } }
            }
          },
          "400": { "description": "Invalid request (bad voice_id, empty text, unsupported format)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientBalance" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/Validation" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/text-to-speech/{voice_id}/stream": {
      "parameters": [
        { "in": "path", "name": "voice_id", "required": true, "schema": { "type": "string" } },
        { "in": "query", "name": "output_format", "required": false, "schema": { "type": "string", "default": "mp3_44100_128" } }
      ],
      "post": {
        "summary": "Stream text to speech",
        "description": "Same body as the synchronous endpoint; audio streams back as it is produced.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TtsBody" } } } },
        "responses": {
          "200": {
            "description": "Streaming audio (chunked)",
            "headers": {
              "X-Request-Id": { "$ref": "#/components/headers/XRequestId" },
              "X-Duration": { "$ref": "#/components/headers/XDuration" }
            },
            "content": { "audio/mpeg": { "schema": { "type": "string", "format": "binary" } } }
          },
          "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/InsufficientBalance" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/history": {
      "get": {
        "summary": "List generation history",
        "responses": {
          "200": {
            "description": "History items, newest first",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "history": { "type": "array", "items": { "$ref": "#/components/schemas/HistoryItem" } } } } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/history/{history_item_id}": {
      "parameters": [{ "in": "path", "name": "history_item_id", "required": true, "schema": { "type": "string" } }],
      "get": {
        "summary": "Download a history item's audio",
        "responses": {
          "200": { "description": "Audio", "headers": { "X-Request-Id": { "$ref": "#/components/headers/XRequestId" } }, "content": { "audio/mpeg": { "schema": { "type": "string", "format": "binary" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "delete": {
        "summary": "Delete a history item",
        "responses": {
          "200": { "description": "Deleted" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/user": {
      "get": {
        "summary": "Current user",
        "responses": {
          "200": { "description": "User account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/user/subscription": {
      "get": {
        "summary": "Current subscription and usage",
        "responses": {
          "200": { "description": "Subscription", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Subscription" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    }
  }
}