{
  "openapi": "3.1.0",
  "info": {
    "title": "Voice Prompt Generator API",
    "version": "1.1.0",
    "description": "Turns a typed IVR script plus a chosen voice into a telephony-ready audio\nfile using ElevenLabs text-to-speech. Built for Broadband Solutions'\nSmart PABX.\n\n### Typical flow\n1. `GET /voices` \u2014 show the caller a voice picker (each voice includes a\n   `preview_url` sample clip you can play directly).\n2. `POST /tts` \u2014 send the script and chosen voice. The response contains a\n   time-limited `audio_url`; download it and save the file with the\n   returned `file_extension` (lowercase) into your prompt library.\n\nIdentical requests are cached: repeating a `POST /tts` with the same text,\nvoice, model, format and settings returns instantly with `cached: true`.\n\n### Audio formats\nThe default `wav_8000` (WAV, PCM 16-bit, mono, 8 kHz) plays natively on\nAsterisk phone systems with no conversion. `GET /formats` lists every\nsupported format. Some high-bitrate formats require a higher ElevenLabs\nplan (noted per format); requesting one the plan doesn't include returns\na 400 error from the upstream provider.\n",
    "contact": {
      "name": "Leigh Brunner",
      "email": "leighbrunner@gmail.com"
    }
  },
  "servers": [
    {
      "url": "https://93i4u23tw7.execute-api.ap-southeast-2.amazonaws.com",
      "description": "Demo environment (Leigh's AWS)"
    },
    {
      "url": "{baseUrl}",
      "description": "Production (Broadband Solutions AWS)",
      "variables": {
        "baseUrl": {
          "default": "https://api.example.com"
        }
      }
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Voices"
    },
    {
      "name": "Generation"
    },
    {
      "name": "Service"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Liveness check",
        "description": "No authentication required.",
        "security": [],
        "tags": [
          "Service"
        ],
        "responses": {
          "200": {
            "description": "Service is up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "service": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "ok": true,
                  "service": "voice-prompt-generator"
                }
              }
            }
          }
        }
      }
    },
    "/voices": {
      "get": {
        "operationId": "listVoices",
        "summary": "List available voices",
        "description": "Returns every voice on the connected ElevenLabs account, flattened for a picker UI. `preview_url` is a ready-to-play MP3 sample hosted by ElevenLabs. The list is cached server-side for one hour.\n",
        "tags": [
          "Voices"
        ],
        "responses": {
          "200": {
            "description": "Voice catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "voices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Voice"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorised"
          }
        }
      }
    },
    "/formats": {
      "get": {
        "operationId": "listFormats",
        "summary": "List supported output formats",
        "description": "Every `output_format` value accepted by `POST /tts`, with file extension, content type, a human label, and the minimum ElevenLabs plan where one applies.\n",
        "tags": [
          "Voices"
        ],
        "responses": {
          "200": {
            "description": "Format catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "formats": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Format"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorised"
          }
        }
      }
    },
    "/tts": {
      "post": {
        "operationId": "generateSpeech",
        "summary": "Generate a voice prompt",
        "description": "Converts `text` to speech in the chosen voice and returns a time-limited download URL (default 1 hour). Results are cached by content: identical requests return instantly with `cached: true`.\n",
        "tags": [
          "Generation"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TtsRequest"
              },
              "examples": {
                "minimal": {
                  "summary": "Phone-system WAV with defaults",
                  "value": {
                    "text": "Welcome to Grand Hotel. For reservations, press one.",
                    "voice_id": "6wdSVG3CMjPfAthsnMv9"
                  }
                },
                "customised": {
                  "summary": "High-quality MP3 with tuned delivery",
                  "value": {
                    "text": "Welcome to Grand Hotel. For reservations, press one.",
                    "voice_id": "6wdSVG3CMjPfAthsnMv9",
                    "model_id": "eleven_multilingual_v2",
                    "output_format": "mp3_44100_128",
                    "voice_settings": {
                      "stability": 0.55,
                      "similarity_boost": 0.75,
                      "speed": 0.95
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Prompt generated (or served from cache).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TtsResult"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (bad text/voice/model/format/settings).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorised"
          },
          "404": {
            "description": "Voice id not found on the connected account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Upstream concurrency limit hit (retried once internally first).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream (ElevenLabs) failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    },
    "responses": {
      "Unauthorised": {
        "description": "Missing or invalid x-api-key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Voice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "6wdSVG3CMjPfAthsnMv9"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Makoto - Japanese male"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "professional"
            ]
          },
          "gender": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "male"
            ]
          },
          "age": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "young"
            ]
          },
          "accent": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "kanto"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "description": "Primary language code.",
            "examples": [
              "ja"
            ]
          },
          "locale": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "ja-JP"
            ]
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "All language codes this voice is verified for."
          },
          "preview_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Ready-to-play MP3 sample hosted by ElevenLabs."
          }
        }
      },
      "Format": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "wav_8000"
            ]
          },
          "label": {
            "type": "string",
            "examples": [
              "WAV 8 kHz (phone system default)"
            ]
          },
          "group": {
            "type": "string",
            "enum": [
              "telephony",
              "wav",
              "pcm",
              "mp3",
              "opus"
            ]
          },
          "file_extension": {
            "type": "string",
            "examples": [
              ".wav"
            ]
          },
          "content_type": {
            "type": "string",
            "examples": [
              "audio/wav"
            ]
          },
          "min_elevenlabs_plan": {
            "type": "string",
            "description": "Present when the format needs a minimum ElevenLabs plan."
          },
          "default": {
            "type": "boolean"
          }
        }
      },
      "VoiceSettings": {
        "type": "object",
        "description": "Optional delivery/quality tuning, passed through to ElevenLabs. Omitted fields use the voice's stored defaults.\n",
        "additionalProperties": false,
        "properties": {
          "stability": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Consistency of delivery. Low values sound more expressive and vary between takes; high values sound steady and even. 0.5 is a sensible middle; higher suits phone menus.\n"
          },
          "similarity_boost": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "How tightly the output adheres to the original voice. Very high values can reproduce recording artefacts; lower slightly if output sounds distorted.\n"
          },
          "style": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Exaggerates the speaker's style. 0 is fastest and most stable; raise only for character-heavy reads.\n"
          },
          "speed": {
            "type": "number",
            "minimum": 0.7,
            "maximum": 1.2,
            "description": "Speaking-rate multiplier. 1.0 is normal; 0.9 to 0.95 works well for IVR menus where callers need time to take options in.\n"
          },
          "use_speaker_boost": {
            "type": "boolean",
            "description": "Boosts similarity to the original speaker at a small latency cost. Usually leave on.\n"
          }
        }
      },
      "TtsRequest": {
        "type": "object",
        "required": [
          "text",
          "voice_id"
        ],
        "properties": {
          "text": {
            "type": "string",
            "maxLength": 1000,
            "description": "The script to speak. Limit configurable server-side (default 1000 chars)."
          },
          "voice_id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{8,64}$",
            "description": "A voice id from GET /voices."
          },
          "model_id": {
            "type": "string",
            "enum": [
              "eleven_multilingual_v2",
              "eleven_flash_v2_5",
              "eleven_turbo_v2_5",
              "eleven_v3"
            ],
            "default": "eleven_multilingual_v2"
          },
          "output_format": {
            "type": "string",
            "description": "Audio container/quality. The telephony group (wav_8000, ulaw_8000, alaw_8000, pcm_8000) plays natively on Asterisk phone systems. GET /formats returns this same list with labels and plan requirements (wav_44100 and pcm_44100 need the ElevenLabs Pro plan; mp3_44100_192 needs Creator).\n",
            "default": "wav_8000",
            "enum": [
              "wav_8000",
              "ulaw_8000",
              "alaw_8000",
              "pcm_8000",
              "wav_16000",
              "wav_22050",
              "wav_24000",
              "wav_44100",
              "pcm_16000",
              "pcm_22050",
              "pcm_24000",
              "pcm_44100",
              "mp3_22050_32",
              "mp3_24000_48",
              "mp3_44100_32",
              "mp3_44100_64",
              "mp3_44100_96",
              "mp3_44100_128",
              "mp3_44100_192",
              "opus_48000_32",
              "opus_48000_64",
              "opus_48000_96",
              "opus_48000_128",
              "opus_48000_192"
            ]
          },
          "voice_settings": {
            "$ref": "#/components/schemas/VoiceSettings"
          }
        }
      },
      "TtsResult": {
        "type": "object",
        "properties": {
          "audio_url": {
            "type": "string",
            "description": "Presigned download URL, valid for one hour by default."
          },
          "file_extension": {
            "type": "string",
            "examples": [
              ".wav"
            ]
          },
          "content_type": {
            "type": "string",
            "examples": [
              "audio/wav"
            ]
          },
          "cached": {
            "type": "boolean",
            "description": "True when served from the content cache without regenerating."
          },
          "characters": {
            "type": "integer"
          },
          "voice_id": {
            "type": "string"
          },
          "model_id": {
            "type": "string"
          },
          "output_format": {
            "type": "string"
          },
          "voice_settings": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/VoiceSettings"
              },
              {
                "type": "null"
              }
            ]
          },
          "sha256": {
            "type": "string",
            "description": "Content hash; identical inputs always produce the same hash."
          },
          "bytes": {
            "type": "integer"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "examples": [
                  "bad_request"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}