Skip to main content

Introduction

Realtime conversation establishes a persistent connection over WebSocket (a protocol that keeps a long-lived, bidirectional connection between client and server), streams your audio or text input to the conversational model in realtime, and the model pushes back text and speech replies incrementally. It suits voice assistants, realtime Q&A, spoken practice, and other scenarios that require back-and-forth interaction. Like realtime transcription, it runs over WebSocket, but the two serve different purposes: Available model:
  • gpt-realtime-2.1: conversational speech model, supports audio and text input, and outputs text and speech replies in realtime.
This API is designed for server-side integration; browsers cannot connect directly. For security reasons, the gateway validates and rejects connections that carry an Origin header, rejects the openai-insecure-api-key subprotocol, and accepts the key only through the standard Authorization header. A browser-initiated WebSocket automatically attaches an Origin header and is therefore rejected. To do realtime conversation in a frontend, establish the connection to the gateway from your own server and forward audio and results between your frontend and server.

Quick Start

Connection endpoint

  • model=gpt-realtime-2.1: required. The model is fixed by the URL parameter at connection time and cannot be changed during the session (see the constraints below).
  • Note the difference from transcription: the conversation endpoint does not take intent=transcription.

Authentication

Pass the key in a standard HTTP header during the handshake:

Audio format requirements

Audio input and output currently support a single format. Convert your audio before sending:
  • Encoding: PCM16 (16-bit signed integer, little-endian)
  • Sample rate: 24000 Hz
  • Channels: mono
That is audio/pcm@24000. Declaring any other format (such as G.711/µ-law) for input or output is rejected and the session is closed.
Unlike transcription, conversation sessions do support voice activity detection (turn_detection / VAD). When enabled, the model decides when an utterance ends and triggers a reply; when disabled (set to null), you control when to commit audio and when to request a reply. Choose either as needed.

Session configuration (session.update)

After the connection is established, the client can send one session.update frame to configure conversation parameters such as the voice, system instructions, and whether VAD is enabled. The conversation model is already anchored by the connection URL, so you can start talking without sending session.update. Send it when you need a custom voice or instructions.

Configuration parameters

string
required
Session type. For conversation it is realtime.
string
System instructions that set the model’s role, tone, and answer constraints.
string[]
Output modalities, either ["audio"] or ["text"]: with ["audio"] (the default) the model outputs speech and the reply text arrives through response.output_audio_transcript.delta; with ["text"] it outputs text only, delivered through response.output_text.delta. Any other combination (for example ["audio", "text"]) is rejected with an error event.
object
required
Input audio format, fixed at { "type": "audio/pcm", "rate": 24000 }.
object | null
Voice activity detection. Pass { "type": "server_vad" } to enable automatic turn detection; pass null to disable it and commit audio and request replies manually from the client.
object
required
Output audio format, fixed at { "type": "audio/pcm", "rate": 24000 }.
string
Voice for the reply audio. It cannot be changed once the first reply starts: after the session enters a generating state, a voice sent again is ignored (other settings still apply). Set the voice before requesting the first reply if you need a specific one.
The following capabilities are not supported in this release and close the session when configured (close code 1008): enabling inline transcription inside a conversation session (audio.input.transcription, reason input_transcription_not_supported), injecting audio (reason item_audio_not_supported) or images (reason image_input_not_supported) through conversation items, and any content item type other than text (reason unsupported_content_part). Send all audio through the input_audio_buffer.append channel.

Sending input

Sending audio

Cut PCM16 audio into small chunks (for example one chunk per 100ms), base64-encode them, and send them continuously with the input_audio_buffer.append event:
With VAD enabled, the model detects the end of an utterance and triggers a reply automatically. With VAD disabled, commit and request a reply manually after sending a segment of audio:

Sending text

You can also inject a text message directly and request a reply:

Receiving replies

The server keeps pushing events. Key event types:
event
Confirmation that the session was created or its configuration updated. You can start sending audio and text once session.created arrives.
event
A conversation item has been written: each user input and each model reply adds one item.
event
With VAD enabled, the server has detected that the user started or stopped speaking. A speech_started event usually means the user is interrupting the model; see Interruption and truncation.
event
A reply has started generating.
event
The reply’s output item has started and finished. The item.id field in the added event is the conversation item ID you reference when truncating audio later.
event
An incremental chunk of the reply audio (base64-encoded PCM16) and its completion marker, which you can play as it arrives.
event
The incremental transcript matching the reply audio sentence by sentence, plus its completion marker; the delta field holds the newly added text. When audio output is enabled, take the reply text from this event, for example to show captions while the audio plays.
event
An incremental chunk of a text-only reply and its completion marker, emitted only when you set the output modality to text only (output_modalities: ["text"]).
event
A reply has finished. This event carries the token usage for the turn (usage), which is what billing is based on.
event
Confirmation that the truncation request took effect; see Interruption and truncation.
event
An error event, with an error code and description. A problem with the request itself (for example an invalid output_modalities value) returns a single error event and the session stays usable; policy issues (such as changing the model or running out of balance) close the session.
Pick the right event for the reply text. By default (output includes audio) the model pushes only response.output_audio_transcript.delta and does not push response.output_text.delta; set the output modality to text only and the text moves to response.output_text.delta. Listen for both in either mode so you never miss text (see run output below).

Interruption and truncation

When the user starts speaking while the model is talking, content that has been generated but not yet played back conflicts with the user’s next sentence. On a WebSocket connection the client handles playback, so the client also completes the cleanup after an interruption. With VAD enabled, the server pushes input_audio_buffer.speech_started once it detects that the user has started speaking. On receiving that event, the client should:
  1. Stop local playback immediately and record how far into the reply it had played (in milliseconds).
  2. Send conversation.item.truncate to remove the unplayed audio from the conversation, so the model does not treat it as spoken in the next turn.
  • item_id: the conversation item ID of this reply, taken from item.id in the response.output_item.added event.
  • content_index: the index of the audio content part, always 0.
  • audio_end_ms: how much audio to keep, in milliseconds, based on where playback actually reached.
The server replies with conversation.item.truncated once the request is processed. Truncation only affects this reply’s audio and its transcript; the session itself is unaffected and you can continue with the next turn. With the OpenAI SDK, call conn.conversation.item.truncate(item_id=..., content_index=0, audio_end_ms=...). With VAD disabled (for example push-to-talk), pressing the button is the interruption: send response.cancel to cancel the in-progress reply and truncate as described above, then on release send input_audio_buffer.append, input_audio_buffer.commit, and response.create in that order.

Complete examples

Three approaches are shown below; pick one:
  • Official OpenAI SDK (recommended): no need to hand-write WebSocket frames; point websocket_base_url (the SDK’s WebSocket base URL parameter) at the gateway and reuse the official library.
  • Official OpenAI Agents SDK: the official agent framework’s realtime voice form; swap the url in model_config for the gateway address.
  • Raw websockets: no SDK, exchange frames directly over the protocol. Fewest dependencies and easiest to debug.
Why pass the model name at connection time? The AiHubMix gateway needs the model at the moment of the WebSocket handshake to select the model provider, authenticate, and reserve quota, while session.update only arrives after the handshake completes. So with an SDK you must pass model explicitly to connect() (the SDK puts it into the URL query); without it the gateway rejects the connection during the handshake and no connection is established. Unlike transcription, the conversation endpoint does not need intent=transcription.
To convert any audio into the raw PCM format this API requires, use ffmpeg:

Reusing official examples

Most realtime conversation examples OpenAI publishes rely only on the SDK’s base URL parameter, so you can reuse them by pointing the address at the AiHubMix endpoint:

Run output (measured in production)

The following is the real output of the OpenAI SDK example against the aihubmix.com production environment with model gpt-realtime-2.1. The session enables server_vad, and both prompts and audio are English. Text input
Audio input
Measured difference between two configurations
Measured confirmation: by default (output includes audio) the reply text arrives character by character only from response.output_audio_transcript.delta, and response.output_text.delta never appears; response.done carries the turn’s usage; the handshake takes about 2 to 4 seconds, the quota reservation cost of establishing the session.

Billing

  • Billed by token: a conversation session returns the token usage for each turn (usage) with the response.done event, and billing is based on it. Usage is metered separately by component, including audio input, audio output, text input, and text output. The unit price of each component follows the live listed price on the model detail page.
  • Settled as you go: this is a long-lived connection, and charges are deducted in realtime with each turn as the session runs, with no single settlement at the end. Establishing a session first makes a quota reservation of about one minute of usage (an admission check only, not an actual charge), and the remaining reservation is released when the session ends. Your account’s available balance must therefore cover at least about one minute of usage for a session to be established.
  • Each realtime conversation billing record can be reviewed entry by entry in Usage and billing.

Limits and constraints

  1. Session duration: a WebSocket connection lasts at most 62 minutes, after which the server closes it (close code 1000, reason session_duration_limit); reconnect in segments if you need longer.
  2. Idle disconnect: when neither the client nor the model has any activity for 5 minutes, the server closes the session (close code 1008, reason idle_timeout). Activity from either side resets the timer, so a long reply that keeps streaming is not interrupted.
  3. Insufficient balance: when establishing a session, if the available balance does not cover the roughly one-minute reservation, the handshake is rejected outright (HTTP 403) and no session is established; during a session, if the balance runs out, the established connection is closed immediately.
  4. Server-side only: direct browser connections are not supported (the Origin header is validated); integrate from your server.
  5. Model locked: the model is fixed in the connection URL, and changing it with session.update during the session is rejected and closes the session.
  6. Format locked: audio input and output support only audio/pcm@24000 mono; other formats are rejected.
  7. Voice locked: voice cannot be changed after the first reply starts; set it before requesting the first reply.
  8. Not yet supported: inline transcription inside a conversation session, injecting audio or image content through conversation items, and any content item type other than text.
  9. One reply at a time: a session allows only one in-progress reply; sending another response.create before the current turn finishes is rejected and closes the session (close code 1008, reason response_already_active).

Common errors


Last updated: 2026-09-21