Realtime API
Provider-neutral dialog, live translation, and live transcription over a metered WebSocket relay.
Yunxin exposes one OpenAI-compatible event surface and adapts it to OpenAI Realtime or Google Gemini Live inside the provider layer. Provider credentials never leave the gateway.
1. Discover routable models
GET /v1/realtime/modelsEvery returned route has passed provider availability, credential, capability, tier, and billing checks.
| Field | Meaning |
|---|---|
id | Public model address to use when creating the session. |
mode | dialog, translation, or transcription. |
voices | Voices accepted by this route. |
input_audio_sample_rate | Required PCM16 microphone rate (Google: 16 kHz; OpenAI: 24 kHz). |
output_audio_sample_rate | PCM16 playback rate, normally 24 kHz. |
pricing_strategy / request_price | Charge basis exposed by the gateway. |
2. Create a model-bound session
POST /v1/realtime/sessions
Content-Type: application/json
{
"model": "public-model-id",
"modalities": ["audio"],
"voice": "alloy",
"instructions": "Answer briefly",
"target_language": "zh-TW"
}target_language applies to Live Translate. Live Transcribe uses modalities: ["text"]. The response
contains websocket_url and a short-lived, single-use client_secret.value. The ticket is signed for
the exact model and, when applicable, the API-key identity that passed authorization.
3. Connect without putting the ticket in the URL
const session = await fetch("/v1/realtime/sessions", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model: "public-model-id", modalities: ["audio"] }),
}).then((response) => response.json());
const root = new URL(location.origin);
root.protocol = root.protocol === "https:" ? "wss:" : "ws:";
const ws = new WebSocket(new URL(session.websocket_url, root), [
"yunxin-realtime",
`yunxin-auth.${session.client_secret.value}`,
]);The ticket is transported as a WebSocket subprotocol so it does not appear in URLs or access logs. It is atomically consumed before the gateway opens the upstream provider socket.
Modes
| Mode | Client input | Public output |
|---|---|---|
| Dialog | PCM audio or conversation.item.create text | Text/audio deltas, transcript, response.done |
| Translation | 16 kHz PCM speech | Input transcript plus translated 24 kHz audio/transcript |
| Transcription | 16 kHz PCM speech | Interim delta and authoritative final input transcription |
Client events
// PCM16 mono audio, base64 encoded
ws.send(JSON.stringify({ type: "input_audio_buffer.append", audio: base64Pcm }));
ws.send(JSON.stringify({ type: "input_audio_buffer.commit" }));
// Text turn (dialog models)
ws.send(JSON.stringify({
type: "conversation.item.create",
item: { role: "user", content: [{ type: "input_text", text: "Hello" }] },
}));
ws.send(JSON.stringify({ type: "response.create" }));Google Live automatically completes a text turn, so its provider adapter consumes the redundant
response.create. Provider operations without an honest equivalent return an explicit
unsupported_event error rather than pretending success.
Server events
| Event | Meaning |
|---|---|
session.created / session.updated | Upstream setup was accepted. |
conversation.item.input_audio_transcription.delta | Interim speech exposed as append-only safe deltas. |
conversation.item.input_audio_transcription.completed | Authoritative final speech segment. |
response.output_text.delta / .done | Streaming/final text. |
response.output_audio.delta / .done | Base64 PCM16 audio and completion. |
response.output_audio_transcript.delta / .done | Spoken output transcript. |
response.done | A dialog response completed. Dedicated transcription is item-based and need not emit this event. |
error | Validation, unsupported event, provider, authorization, or billing failure. |
Client events are limited to 1 MiB and decoded audio chunks to 256 KiB. Paid token-priced realtime routes remain hidden until their modality-specific charge is representable; free and explicit per-request routes can be exposed safely today.
How is this guide?