OPENAI_BASE_URL at the gateway and its OPENAI_API_KEY at a LangWatch virtual key. Voice traffic gets the same governance as chat: virtual-key auth, model allowlists, budgets, rate limits, and per-call observability.
Two providers ship today:
Configure the provider key once in Settings → Model Providers (OpenAI and ElevenLabs both take a plain API key); every virtual key routed to that provider can then call its voice models.
Text to speech
voice field.
Content-Type (audio/mpeg, audio/wav, audio/pcm, …) with no JSON envelope, exactly like OpenAI, so client.audio.speech.create(...) consumes it unchanged. response_format: "pcm" returns raw PCM16 for realtime consumers.
Transcription
file part plus model, and optionally language, prompt, response_format, temperature. Uploads are capped at 25 MB (matching OpenAI’s own limit); larger uploads get a 413 before any provider is contacted.
text, plus whatever the provider reports (duration, segments, token usage).
ElevenLabs’ own audio paths
The two routes above take OpenAI’s request shape. If your code already uses the ElevenLabs SDK, you do not have to rewrite it: the gateway also serves that vendor’s own paths, so two settings are the whole change. Point the SDK’s base URL at the gateway, and give it a LangWatch virtual key in place of your ElevenLabs key. Your ElevenLabs key stays where it belongs, configured once in Settings → Model Providers; an SDK still holding it will be rejected, because the gateway reads that header as a virtual key.xi-api-key header the SDK already sends, so no code changes. Bodies, form parts and query parameters reach ElevenLabs as you wrote them, so voice settings, output_format, diarization, timestamp granularity and the rest keep working. The one field the gateway changes is model_id: it is replaced with whatever the virtual key’s aliases and allowlist resolve the name to, so elevenlabs/eleven_flash_v2_5 and a key alias both reach the vendor as the bare model it knows. A synthesis request that names no model_id bills and gates under eleven_multilingual_v2, which is what the vendor would have used.
file part, or a cloud_storage_url part for ElevenLabs to fetch the audio itself. Uploads are capped at 25 MB of audio, the same limit as /v1/audio/transcriptions; larger uploads get a 413 before any provider is contacted. Send a cloud_storage_url for a bigger file: ElevenLabs fetches it directly and the gateway never holds it.
The gateway refuses asynchronous transcription. A truthy webhook part gets a 400 from the gateway itself, before ElevenLabs is contacted at all, so no call is made and nothing is billed. The reason for the refusal is that the mode makes the vendor answer before it has transcribed anything: were the request forwarded, the reply would carry no duration and the transcription would meter as free.
Metering is the same as on the OpenAI-shaped routes: synthesis is billed by the Unicode characters of text, counted as characters rather than bytes so an accented or non-Latin script costs what it reads; transcription is billed by the audio duration ElevenLabs reports on its own answer. A call costs the same whichever of the two wires it arrives on.
Streaming (/v1/text-to-speech/{voice_id}/stream) is not served yet.
Observability and cost measures
Every audio call lands as a gateway trace like chat does. Providers that report token usage (gpt-4o-mini-tts, gpt-4o-transcribe) fill the standard gen_ai.usage.* token attributes; character- and duration-priced providers are measured by two audio-specific attributes:
Errors
Same error surface as every other endpoint; see Errors. Provider rejections (an invalid ElevenLabs voice id, an unsupported format) pass through with the provider’s own status code and body. A model outside the virtual key’s allowlist returns the standardmodel_not_allowed; a provider with no key configured returns no_provider_configured.
Realtime voice
OpenAI Realtime and ElevenLabs Conversational AI run over a websocket rather than these request and response routes. The gateway brokers those sessions: it mints the vendor’s own session credential on your virtual key, under your budgets, and the media socket runs from your client to the vendor. See Realtime voice.Not yet supported
- Streaming TTS/STT (
stream=true): requests are served complete; streaming is a follow-up.