A translation session is a live session whose model is a simultaneous interpreter rather than a conversation partner. Speech goes in; the same words in another language come out. The model never answers, comments, greets, or takes a turn of its own. WS /v1/realtime/translations is the endpoint. In the console it is Studio → Translate.

When to use it

A meeting in two languages

Everyone speaks their own and hears their own. Leave the source language unset and it follows whoever is talking.

Speaking to a room

Give it your cloned voice and the room hears you in their language.

Support across a language line

One agent, one caller, no interpreter booked in advance.

Not for a recorded file

Dubbing a recording is transcription plus synthesis, not a live session. Transcribe it, translate the text, then synthesize it.

Why this is a session type and not a prompt

Every conversational session carries the platform persona, and the persona instructs the model to introduce itself and — in as many words — to translate itself only when asked. Your instructions are appended after it, so a translation prompt arrives behind an identity that contradicts it, and loses. The observable failure is not a bad translation. It is the model answering the speaker: given “Good morning everyone, where is the train station?” it replies with a greeting in the target language and nothing of what was actually said. So interpretation is its own mode, and the persona is skipped for it — not softened, skipped. The two prompts describe incompatible jobs and there is no useful blend of them.

Connect

Read the model id from GET /v1/audio/models rather than hardcoding it — realtime is opt-in per environment, and a deployment with no realtime backend serves no realtime model at all.
Resume does not work on this endpoint. /v1/realtime/translations declares no resume or resume_token query parameter, so a redial carrying them is answered with a brand-new session — a fresh pipeline, an empty conversation, no resumed field. The eesi.session greeting is the shared one and still says resume.supported: true, so nothing tells you it was ignored. A client that trusts the greeting reconnects and believes it recovered a conversation the server has already forgotten.Treat a dropped interpreting socket as a new session: mint a fresh ticket, redial without the resume parameters, and set the languages again. Tickets and eesi.keepalive do work here exactly as they do on /v1/realtime.

Choose the languages

Either in the URL, as above, or in session.update — which is also how you re-target a session that is already running:
Omitting the source is the right default for a meeting. Speakers switch languages mid-conversation, and a pinned source turns every other speaker into gibberish. With none, each utterance is detected on its own, and an utterance already in the target language produces no output at all. Language settings accumulate across frames, so a later session.update that changes only the voice keeps the direction you set earlier. Region subtags are tolerated (pt-BR reads as Portuguese). Use a code from the supported set — an unrecognised code is passed to the model as the literal code, which is not a language name and interprets badly. Both spellings also work on the ordinary /v1/realtime endpoint: setting session.type to "translation", or naming a target language, switches that session to interpreting. A session connected to /v1/realtime/translations with no language named stays inert — neither persona nor interpreter prompt — until a session.update names one, which is how OpenAI’s own client configures itself after connecting.
Name the target language in the same frame as "type": "translation", or put it in the URL. The gateway rewrites translation back to realtime before the frame goes upstream, because the upstream validator accepts no other literal — but it only does that once it knows a target language. A frame that declares the type with no language, and no language set earlier, reaches the validator as "translation" and is rejected whole, taking your voice and instructions with it. You get one error with type unknown_or_invalid_event.

Choose the voice

An interpreting session takes a voice exactly like a conversational one, in the same place, and cloned voices work here too:
Clone your own voice and the room hears you speaking the target language, which is the version of this feature people actually want. See Voices.
Never send "voice": null on a later update. session.update merges partially, so a null erases the voice already set — and with no speaker conditioning the synthesis model samples a new voice on every utterance, so the interpreter changes speaker mid-conversation. The gateway pins the platform default when you name no voice at all; this failure is only reachable by explicitly clearing one.

Defaults you get without asking

Set audio.input.turn_detection yourself and your choice is kept — on that frame and on every later one, even ones that do not mention it. That is the trap: the moment you send a block of your own, the injected default stops arriving entirely, so carry interrupt_response: false over yourself or every sentence loses its tail. The semantic_vad type and its eagerness are inert on this backend — the server runs its own detector and reads only threshold, silence_duration_ms and interrupt_response — so replacing the block with server_vad changes nothing except what you now have to set. See Turn-taking.

Text in, text out

Input and output modalities are independent, so all four combinations run on one session and one connection. Ask for text only on the session, or per response:
A text-only session skips synthesis entirely rather than rendering audio nobody plays. That is the reason to ask for it: speech is the slowest stage of a turn, so text out is not a formatting preference — it is the difference between a translation that lands in a moment and one that waits for a voice.
Text has no voice activity detection behind it, so a conversation.item.create does not start a turn. Follow it with {"type": "response.create"}. Audio needs no nudge.

Refine it

Anything in instructions is appended after the interpreter prompt, where trailing weight makes it an override — useful for domain vocabulary or a register:

What it leaves behind

An interpreting session is an ordinary session row, so it lists, replays and deletes like any other — with one addition. The row records what it interpreted: the final language pair, which may only have been named mid-call, and the volume of output. That makes “was this a translation session?” one field to check. See Sessions.

Next

Live

The transport and events are identical; the prompt is what differs.

Voices

Clone the speaker so the room hears them, not a stranger.

Connections that survive

Tickets, keepalives and close codes apply here unchanged. Resume does not.

Latency

Why text out is faster, and what else moves the number.