{
  "openapi": "3.1.0",
  "info": {
    "title": "TTS Library API",
    "description": "Read-only, unauthenticated API for a directory of text-to-speech voices across multiple providers (Telnyx, MiniMax, Inworld, Azure, Rime, Resemble, AWS Polly, FishAudio, XAI, and more). No API key required; no write operations exist. Every response carries an `X-API-Version` header matching this spec's version. Requests are rate-limited per IP; see the `X-RateLimit-*` response headers.\n\n**Versioning policy:** unversioned `/api/*` paths and the `/v1/*` aliases are equivalent and both considered stable. If a breaking change is ever needed, it will ship under `/v2/*` while `/v1/*` (and `/api/*`) continue to work.",
    "version": "1.1.0",
    "contact": {
      "url": "https://ttslibrary.com/about"
    },
    "license": {
      "name": "See Terms of Service",
      "url": "https://ttslibrary.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://ttslibrary.com",
      "description": "Production"
    }
  ],
  "security": [],
  "paths": {
    "/api/voices": {
      "get": {
        "operationId": "listVoices",
        "summary": "Search and filter the voice catalog",
        "description": "Filters, deduplicates, and paginates the voice catalog. Results are sorted with Telnyx Ultra voices first.",
        "parameters": [
          { "name": "search", "in": "query", "schema": { "type": "string" }, "description": "Full-text search across name, ID, language, provider, and accent." },
          { "name": "providers", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated provider filter, e.g. `telnyx,aws,azure,minimax,inworld,rime,resemble,fishaudio,xai`." },
          { "name": "genders", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated gender filter, e.g. `Female,Male,Neutral`." },
          { "name": "language", "in": "query", "schema": { "type": "string" }, "description": "Language name filter, e.g. `Spanish`." },
          { "name": "country", "in": "query", "schema": { "type": "string" }, "description": "Country name filter, e.g. `Mexico`." },
          { "name": "model", "in": "query", "schema": { "type": "string" }, "description": "Model/tier name filter, e.g. `Ultra`, `speech-2.6-turbo`." },
          { "name": "accent", "in": "query", "schema": { "type": "string" }, "description": "Accent filter, e.g. `American`, `British`." },
          { "name": "ages", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated age filter, e.g. `adult`." },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 48 }, "description": "Results per page." },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 }, "description": "Pagination offset." }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of voices matching the filters.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/VoiceListResponse" },
                "example": {
                  "voices": [
                    {
                      "id": "Telnyx.Ultra.Bella",
                      "name": "Bella",
                      "language": "en-US",
                      "provider": "telnyx",
                      "model_id": "Ultra",
                      "gender": "Female",
                      "uniqueKey": "Telnyx.Ultra.Bella",
                      "accent": "American",
                      "description": "Telnyx Ultra American English female voice",
                      "language_name": "English",
                      "country_name": "United States"
                    }
                  ],
                  "total": 4456,
                  "limit": 48,
                  "offset": 0,
                  "hasMore": true
                }
              }
            },
            "headers": {
              "X-API-Version": { "$ref": "#/components/headers/ApiVersion" },
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "500": {
            "description": "Internal server error.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    },
    "/api/metadata": {
      "get": {
        "operationId": "getMetadata",
        "summary": "Get available filter values and catalog statistics",
        "description": "Returns all distinct provider, gender, language, country, and accent values present in the catalog, derived dynamically at request time.",
        "responses": {
          "200": {
            "description": "Filter values and aggregate stats.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MetadataResponse" },
                "example": {
                  "total_voices": 4456,
                  "providers": ["aws", "azure", "fishaudio", "humain", "inworld", "minimax", "resemble", "rime", "telnyx", "xai"],
                  "genders": ["Female", "Male", "Neutral"],
                  "languages": ["Afrikaans", "Albanian", "..."],
                  "countries": ["Afghanistan", "Algeria", "..."],
                  "accents": ["Algerian Arabic", "American", "..."],
                  "stats": {
                    "total_voices": 4456,
                    "total_languages": 92,
                    "total_countries": 115,
                    "total_providers": 10,
                    "total_accents": 264
                  }
                }
              }
            },
            "headers": {
              "X-API-Version": { "$ref": "#/components/headers/ApiVersion" },
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "500": {
            "description": "Internal server error.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    },
    "/api/audio-url": {
      "get": {
        "operationId": "getAudioUrl",
        "summary": "Get a playable audio preview URL for a voice",
        "description": "Returns a presigned URL for the voice's audio preview file in Telnyx Cloud Storage.",
        "parameters": [
          { "name": "voiceId", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Voice ID from the catalog, e.g. `AWS.Polly.Joanna-Neural`." }
        ],
        "responses": {
          "200": {
            "description": "Presigned (or fallback CDN) audio URL.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AudioUrlResponse" },
                "example": {
                  "url": "https://us-central-1.telnyxcloudstorage.com/tts-directory/audio/AWS_Polly_Joanna-Neural.mp3",
                  "voiceId": "AWS.Polly.Joanna-Neural",
                  "filename": "AWS_Polly_Joanna-Neural.mp3",
                  "source": "presigned"
                }
              }
            },
            "headers": {
              "X-API-Version": { "$ref": "#/components/headers/ApiVersion" },
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }
            }
          },
          "400": {
            "description": "Missing `voiceId` query parameter.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "500": {
            "description": "Internal server error.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    },
    "/v1/voices": {
      "get": {
        "operationId": "listVoicesV1",
        "summary": "Search and filter the voice catalog (v1 alias)",
        "description": "Identical to `GET /api/voices`, provided for explicit version pinning. See the versioning policy in the spec description.",
        "parameters": [
          { "name": "search", "in": "query", "schema": { "type": "string" }, "description": "Full-text search across name, ID, language, provider, and accent." },
          { "name": "providers", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated provider filter, e.g. `telnyx,aws,azure,minimax,inworld,rime,resemble,fishaudio,xai`." },
          { "name": "genders", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated gender filter, e.g. `Female,Male,Neutral`." },
          { "name": "language", "in": "query", "schema": { "type": "string" }, "description": "Language name filter, e.g. `Spanish`." },
          { "name": "country", "in": "query", "schema": { "type": "string" }, "description": "Country name filter, e.g. `Mexico`." },
          { "name": "model", "in": "query", "schema": { "type": "string" }, "description": "Model/tier name filter, e.g. `Ultra`, `speech-2.6-turbo`." },
          { "name": "accent", "in": "query", "schema": { "type": "string" }, "description": "Accent filter, e.g. `American`, `British`." },
          { "name": "ages", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated age filter, e.g. `adult`." },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 48 }, "description": "Results per page." },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 }, "description": "Pagination offset." }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of voices matching the filters.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VoiceListResponse" } } },
            "headers": {
              "X-API-Version": { "$ref": "#/components/headers/ApiVersion" },
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "500": {
            "description": "Internal server error.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    },
    "/v1/metadata": {
      "get": {
        "operationId": "getMetadataV1",
        "summary": "Get available filter values and catalog statistics (v1 alias)",
        "description": "Identical to `GET /api/metadata`, provided for explicit version pinning. See the versioning policy in the spec description.",
        "responses": {
          "200": {
            "description": "Filter values and aggregate stats.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MetadataResponse" } } },
            "headers": {
              "X-API-Version": { "$ref": "#/components/headers/ApiVersion" },
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "500": {
            "description": "Internal server error.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    },
    "/v1/audio-url": {
      "get": {
        "operationId": "getAudioUrlV1",
        "summary": "Get a playable audio preview URL for a voice (v1 alias)",
        "description": "Identical to `GET /api/audio-url`, provided for explicit version pinning. See the versioning policy in the spec description.",
        "parameters": [
          { "name": "voiceId", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Voice ID from the catalog, e.g. `AWS.Polly.Joanna-Neural`." }
        ],
        "responses": {
          "200": {
            "description": "Presigned (or fallback CDN) audio URL.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AudioUrlResponse" } } },
            "headers": {
              "X-API-Version": { "$ref": "#/components/headers/ApiVersion" },
              "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }
            }
          },
          "400": {
            "description": "Missing `voiceId` query parameter.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "500": {
            "description": "Internal server error.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Voice": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "description": "Unique voice identifier." },
          "name": { "type": "string", "description": "Human-readable voice name." },
          "language": { "type": "string", "description": "BCP-47 language-region code." },
          "provider": { "type": "string", "description": "TTS provider slug." },
          "model_id": { "type": "string", "description": "Provider model/tier name, when applicable." },
          "gender": { "type": "string", "enum": ["Female", "Male", "Neutral"] },
          "accent": { "type": "string" },
          "description": { "type": "string", "description": "Short description, up to 90 characters." },
          "language_name": { "type": "string" },
          "country_name": { "type": "string" },
          "uniqueKey": { "type": "string" }
        },
        "required": ["id", "name", "language", "provider", "gender"]
      },
      "VoiceListResponse": {
        "type": "object",
        "properties": {
          "voices": { "type": "array", "items": { "$ref": "#/components/schemas/Voice" } },
          "total": { "type": "integer" },
          "limit": { "type": "integer" },
          "offset": { "type": "integer" },
          "hasMore": { "type": "boolean" }
        },
        "required": ["voices", "total", "limit", "offset", "hasMore"]
      },
      "MetadataResponse": {
        "type": "object",
        "properties": {
          "total_voices": { "type": "integer" },
          "providers": { "type": "array", "items": { "type": "string" } },
          "genders": { "type": "array", "items": { "type": "string" } },
          "languages": { "type": "array", "items": { "type": "string" } },
          "countries": { "type": "array", "items": { "type": "string" } },
          "accents": { "type": "array", "items": { "type": "string" } },
          "stats": {
            "type": "object",
            "properties": {
              "total_voices": { "type": "integer" },
              "total_languages": { "type": "integer" },
              "total_countries": { "type": "integer" },
              "total_providers": { "type": "integer" },
              "total_accents": { "type": "integer" }
            }
          }
        }
      },
      "AudioUrlResponse": {
        "type": "object",
        "properties": {
          "url": { "type": "string", "format": "uri" },
          "voiceId": { "type": "string" },
          "filename": { "type": "string" },
          "source": { "type": "string", "enum": ["presigned", "fallback-cdn"] }
        },
        "required": ["url", "voiceId", "filename"]
      },
      "Error": {
        "type": "object",
        "description": "Typed error shape returned by every endpoint on failure.",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "string", "description": "Machine-readable error code, e.g. `rate_limited`, `missing_voice_id`, `internal_error`." },
              "message": { "type": "string", "description": "Human-readable explanation." }
            },
            "required": ["code", "message"]
          }
        },
        "required": ["error"]
      }
    },
    "headers": {
      "ApiVersion": {
        "description": "The API version that generated this response, matching this spec's `info.version`.",
        "schema": { "type": "string", "example": "1.1.0" }
      },
      "RateLimitLimit": {
        "description": "Requests allowed per window.",
        "schema": { "type": "integer" }
      },
      "RateLimitRemaining": {
        "description": "Requests remaining in the current window.",
        "schema": { "type": "integer" }
      },
      "RateLimitReset": {
        "description": "Unix timestamp (seconds) when the current window resets.",
        "schema": { "type": "integer" }
      }
    }
  }
}
