Skip to main content
OpenAI-compatible audio endpoints. Any client that speaks OpenAI’s audio API (the official SDKs, LiveKit and Pipecat voice agents, Scenario’s voice testing harness) works with zero code change by pointing its 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

Body matches OpenAI’s speech schema. For ElevenLabs models, put the ElevenLabs voice id in the same voice field.
The response body is the raw audio bytes with the matching 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

Form matches OpenAI’s transcription schema: a 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.
The response is the standard JSON transcript with 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.
The virtual key goes in the 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.
These routes need the virtual key to reach an ElevenLabs provider. The vendor key itself lives on the organization’s ElevenLabs provider row, never on the virtual key, so what matters is that the key’s provider access still includes ElevenLabs. They carry that vendor’s own request shape, so the gateway will not fall back to another provider for them: a key that cannot reach ElevenLabs is refused rather than served by whichever provider it does hold. Transcription accepts a 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 standard model_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.
Last modified on August 22, 2026