wss://prism.api.rime.ai/v1/realtime from your backend, and send your API key on the upgrade request as Authorization: Bearer YOUR_API_KEY. Each connection is one conversation. Your backend sends the caller’s audio as base64 16 kHz mono PCM16. Prism finds the end of each caller turn, and replies with text and with 24 kHz audio in the same format.
Use this API when your backend uses a language other than Python, Node.js, Go, or Rust. Otherwise, the Rime SDK implements this protocol for you. The WebSocket quickstart makes a first call with a plain WebSocket client, and Errors and limits tells you what to do about each error code.
Session at a glance
Guarantees
Complete example
This Python client holds one short conversation with the raw WebSocket API. It streams a recorded question at speaking pace, then keeps sending silence, as a microphone does. When Prism asks for the cafe’s opening hours, the client runsget_opening_hours, returns the result, and asks Prism to continue, as Tools explains. It prints the caller’s transcript, the tool call, and Prism’s replies, and saves the spoken replies to reply.wav. It stops with an error if Prism refuses one of its requests or a reply does not complete. It also stops if no event arrives for 30 seconds while it waits for a reply.
The client does not play audio or report playback. A live client does both, as Stop playback when the caller talks over a reply explains.
The client needs Python 3.11 or later, and websockets 14 or later for the additional_headers argument. Before you run it, do these steps:
- Install the package with
pip install "websockets>=14". - Set
RIME_API_KEYin the environment of your backend. - Download the quickstart’s sample question.
input.m4a, or any format that FFmpeg can decode. It writes question.wav as 16 kHz mono PCM16, and it keeps the WAV header.
prism_ws.py
prism_ws.py
prism_ws.py
python prism_ws.py question.wav. A run with the sample question printed this output:
Prism: line of your run shows it too. reply.wav holds both replies, in the aurora voice. If a run fails, send the request and session IDs to support.
Rime SDK steps as WebSocket events
Each step in Live conversations has a WebSocket event. The tool steps are in Tools.
The SDK handles two waits. Without the SDK, your backend must handle them:
- Send
session.updatebefore any audio orresponse.create, and then wait forsession.updated, which confirms that Prism accepted the update. Until Prism accepts it, Prism refuses both withsession_not_initialized. - Send a typed turn or a nudge only after
prism.typed_input.ready. If Prism refuses it as busy, send it again after the nextprism.typed_input.ready. Prism does not keep a refused request for later.
Connect
Authentication
SendAuthorization: Bearer YOUR_API_KEY on the upgrade request. The WebSocket API in a browser cannot set this header. Open the connection from your backend, and keep the key there. Prism has no ephemeral keys or tokens for client devices. Browsers and phones send their audio to your backend, and your backend sends it to Prism.
Upgrade refusals
Prism can refuse the upgrade with an HTTP response, before any event, and noerror event follows. A successful upgrade returns 101 Switching Protocols.
Close codes
Audio appends do not count as activity, so a conversation that streams only silence still closes after 5 minutes. Any other client event, detected speech, or a reply starts the timer again.
Each reconnect starts a new conversation with no memory of the old one. Send
session.update again, and add the history that the agent needs with conversation.item.create messages.
Audio formats
Send the caller’s audio all the time, as Stream audio for the whole call explains. Include silence, because Prism uses the audio after the caller stops to decide that the turn has ended. Keep sending while Prism speaks, because Prism hears an interruption only in the audio that it receives. While the caller is muted, send silence. If no audio arrives for several seconds, turn detection works less well for the rest of the call, and Prism sends no error.
The reply text holds every word of the reply audio, including these short lines that Prism adds to a spoken reply:
Use the text for captions and logs. A reply can also have no audio at all. If a reply makes only tool calls and says no phrase, the reply has no audio, no
response.audio.done, and no prism_drain_token. Prism says no phrase in a tool continuation, in a nudge with prism_call_id, or for a tool with prism_filler {"type": "none"}.
Session configuration
These are the fields of thesession object in session.update. Every field is optional. In the first update, a field that you leave out takes its default. In a later update, a field that you leave out keeps its current value. Prism refuses any other field with invalid_client_event, and the error’s param names the field.
A later update can change these fields:
These fields keep the values from the first update. A later update can repeat the current value, but Prism refuses a new one:
Prism reads the tool definitions itself, so say in
instructions when to call each tool, as Tools explains. parameters is a JSON Schema object. Each tool can use either OpenAI shape, and if a tool has both, Prism uses the nested function:
- Flat:
{"type": "function", "name": ..., "description": ..., "parameters": {...}} - Nested:
{"type": "function", "function": {"name": ..., "description": ..., "parameters": {...}}}
session.update that passes its checks, and that update sets up the session. After that, you can send session.update at any time, even while a reply runs. Prism refuses a new eagerness or prism_end_of_turn_timeout_ms with session_setting_fixed, and a new modalities value with unsupported_modality. Prism rejects the entire update if any field is invalid. Correct the field and send the update again. A reply that is already running keeps the settings that it started with, and the next reply uses the new settings. prism_settings_revision in the session object counts the updates that Prism accepted, and each reply carries the revision that it started with.
When the conversation outgrows the context
Withtruncation "auto", Prism leaves whole turns out of the prompt, oldest first, when the conversation no longer fits the context. Prism cuts until the history fills at most 80% of the context left after the instructions, tools, current turn, and reply. It cuts more than it must, so that the next turns keep the same cut and stay fast. A retention_ratio sets a share other than 80%. The turns that Prism leaves out stay in the conversation, and prism.conversation.get still lists them. When Prism leaves turns out of a reply’s prompt, that reply’s response.done says so in prism_truncation. A system message that you add goes out with its turn, so put lasting rules in instructions. Truncation leaves out only history, so if the instructions, tools, and current turn alone leave no room for the 512-token reply, the reply fails with context_length_exceeded.
Turns and interruptions
How a turn ends explains how Prism ends the caller’s turns, whatinterrupt_response changes, and how your backend handles an interruption.
Report playback
Stop playback when the caller talks over a reply explains when to sendconversation.item.truncate, and Report when a reply finishes playing explains prism.playback.drained.
Tools
Tools explains a full tool round, from the call to the continuation. Timeouts and late results explains the two waits thatprism_tool_waits reports. Over the WebSocket API, a tool round uses these events:
Without the SDK, wait for these events before you continue:
- The
response.doneof the parent reply - A
conversation.item.createdfor every result
tool_continuation_not_ready, and does not keep the early continuation for later. The complete example runs one tool round this way.
Typed turns, nudges, and history
Typed turns and nudges explains when Prism accepts aresponse.create without new speech, and how to add history with conversation.item.create. Over the WebSocket API, you can also remove items with conversation.item.delete and list the conversation with prism.conversation.get.
Client events
Any client event can carry anevent_id, a string that you choose, such as "evt_setup". No client event requires one. When a request has one, Prism copies it into prism_request_event_id on the acknowledgment, or into error.event_id on a refusal. Prism runs a request without an event_id too. Its acknowledgment then has no prism_request_event_id, and a refusal of it has error.event_id null. Prism refuses an event_id that is not a string, such as null or a number, with invalid_event. Prism does not check that an event_id is unique.
Send an event_id on session.update, conversation.item.create, conversation.item.delete, prism.conversation.get, and response.create. Your backend waits for the answer to each of these, and the ID matches each answer to its request. Without an event_id, Prism suppresses these refusals: tool_call_not_found for a tool result that names a call this conversation never made, and response_superseded or nudge_superseded for a requested reply that does not run because the caller spoke first.
session.update
Sets up the conversation, and later changes its settings. Send the first update right aftersession.created. Prism answers with session.updated when the session is ready, up to 5 seconds later. Prism keeps audio and response.create that you send after Prism accepts the first update, even before session.updated arrives. Before Prism accepts an update, it refuses both with session_not_initialized. Send another update at any time to change a field that Session configuration lists as changeable, such as the voice. Prism answers each accepted update with session.updated, and the next reply uses the new settings.
Refusals:
invalid_client_event, invalid_session, unsupported_modality, invalid_voice, voice_required, and session_setting_fixed. A refused update changes nothing.
The first update sets up the session:
input_audio_buffer.append
The caller’s audio, as you capture it. Prism sends no acknowledgment, and an append does not count as activity for the idle timeout.
Refusals:
invalid_audio for audio that is not base64, and session_not_initialized for audio that arrives before Prism accepts a session.update. Prism drops that early audio. It reports the first dropped append at once, and then sends at most one report every 5 seconds.
The base64 value is shortened here:
input_audio_buffer.clear
Drops the caller’s speech that Prism has not committed as a turn, including speech that Prism holds while a reply plays, and resets speech detection. If Prism already sent aspeech_started for that speech, no speech_stopped or committed follows. Prism answers with input_audio_buffer.cleared.
conversation.item.create
Adds a tool result, a message, or a past tool call to the conversation, without starting a reply. Prism answers withconversation.item.created. The new item goes at the end of the conversation, unless previous_item_id puts it somewhere else.
A
function_call_output item returns the result of a tool:
tool_call_not_found for a call that this conversation did not make, and conflict for a second, different result. If a result for an unknown call has no event_id, Prism drops it with no error.
A message item adds history, and it stays in the history until you delete it with conversation.item.delete. Its words can be up to 4,000 code points, and Prism refuses a longer message with item_too_long.
msg_client_1:
function_call item adds a past tool call, for example one from an earlier conversation. Prism shows the call to the model only after you add its result as a function_call_output with the same call_id. A past call never starts a tool round.
response.create
Asks for a reply without new speech.response.metadata.prism_cause names the kind of reply, and Prism refuses a missing or unknown value with invalid_response_cause. Prism checks the whole request first, so a refused request changes nothing. Prism answers an accepted request with response.created. For a typed turn, Prism first sends conversation.item.created for the caller’s item.
A typed turn:
response.cancel
Stops the running reply. If you setresponse_id, Prism stops the reply only if it is the running reply. The response.done of that reply follows, with status cancelled and reason client_cancelled, and it carries the event_id of this request as prism_request_event_id. After that, the next waiting reply starts.
Refusals:
response_cancel_not_active when no reply with that ID is running. You cannot cancel a reply that is still waiting to start.
- Prism removes the words of the cancelled reply from the history, unless the reply made tool calls.
- Prism’s next reply does not acknowledge an interruption.
- Prism does not undo tool calls that it already sent.
conversation.item.truncate
Reports how much of the reply audio the caller heard. Send it after an interruption, as What Prism remembers of an interrupted reply describes. Prism answers withconversation.item.truncated. The report also lets Prism acknowledge the interruption with a word, such as “Okay,” at the start of its next spoken reply. Prism does this when the report shows that the caller did not hear 300 ms or more of the reply audio.
Refusals:
item_not_found for an unknown item_id, invalid_content_index for a content_index other than 0, and truncate_not_current for a reply from an earlier period of playback.
conversation.item.delete
Removes an item from the conversation. The next reply no longer sees the item, but a reply that is already running still uses it. Prism answers withconversation.item.deleted at once, even while a reply runs, and even for an item that is already deleted.
Refusals:
item_not_found for an ID that this conversation never had.
- A tool call or its result: when you delete one of them, Prism removes both from what the model sees. The other calls of the same reply stay.
- One part of a spoken turn:
prism.conversationcan list a spoken turn in several parts, each with its transcript. When you delete one part, the model no longer sees its transcript. The model still hears the audio of the turn while another part of the turn remains. - A tool call that waits for its result: after you delete the call, the tool round still waits. When the result arrives, Prism acknowledges it with
conversation.item.created, but the model never sees the call or the result. - An item with an ID that you chose: after the delete, you can give that ID to a new item.
prism.conversation.get
Requests the stored conversation, including turns that truncation may omit from the next prompt. Prism answers withprism.conversation at once, even while a reply runs.
prism.playback.drained
An optional receipt that tells Prism that your player played the last sample of a reply. Prism does not acknowledge it. Only the most recent reply that sent audio has a live token, and Prism ignores a receipt with any other token.prism.client.turn_metrics, a timing report from the client, with an optional event_id. Prism does not read or acknowledge the report, and you do not need to send it.
Server events
Every server event has its own uniqueevent_id, such as "evt_4a4fe962c23bf40b". The tables below list the other fields. Long values in the examples are shortened.
session.created
The first event on every connection, with the session’s defaults. Itsprism_settings_revision is 0.
session.updated
Prism sends this event for everysession.update that it accepts, including an empty update or one that repeats the current values. The event contains the whole session, with the settings in effect and the new prism_settings_revision. After the first update, prism.typed_input.ready follows, because a new conversation is idle.
This event answers the first update, which sets up the session:
The session object
session.idle_timeout
The conversation was idle foridle_timeout_s seconds. Prism closes the connection with 1000 “idle” immediately after this event.
prism.typed_input.ready
Prism is idle and can accept a typed turn or a nudge now. Prism sends this event aftersession.updated and each time it becomes idle again, but does not repeat it while it stays idle. The event is true only at the moment Prism sends it, because the caller can start to speak before your request arrives. Treat typed input as unavailable from response.created, input_audio_buffer.speech_started, or an accepted typed turn until the next prism.typed_input.ready.
The event has no fields other than type and event_id:
input_audio_buffer.speech_started
The caller started to speak. When interruption is on, stop playback and drop the queued audio on everyspeech_started, even if response.done already arrived. Prism also cancels any reply that it is still producing.
input_audio_buffer.speech_stopped
The caller stopped speaking.input_audio_buffer.committed for the same item follows, unless pending_speech_overflow discards the turn.
Prism can add values later, so accept a value that you do not know.
input_audio_buffer.committed
Prism committed the caller’s turn, and a reply to the turn follows. Prism can commit more turns while the next reply waits to start, and that reply answers all of these turns together.prism.input_audio_buffer.speech_hint
An early, advisory hint that the caller may have started to speak during a reply, while the reply can still be playing. The Rime SDKs deliver it asSpeechHint in Python and speech.hint in Node.js. Lower playback on the early speech hint tells you how to use it.
input_audio_buffer.cleared
Acknowledgesinput_audio_buffer.clear.
conversation.item.input_audio_transcription.delta
More of the caller’s words, for display while the caller speaks. Prism replies to the caller’s audio, not to these transcripts.conversation.item.input_audio_transcription.completed
The caller’s words for the whole turn. It can arrive before or after the reply to the turn. If this transcript differs from the deltas, use this transcript, because Prism keeps this one in the history.conversation.item.input_audio_transcription.failed
Prism could not make a transcript of the turn, and the reply continues without it. This is not anerror event.
response.created
A reply started.response.output_item.added for the reply’s message follows immediately.
This reply answers a spoken turn:
response.output_item.added. The reply has a first-output timeout of 20 seconds. If no slot becomes free in that time, the reply fails with capacity_exceeded.
response.output_item.added
One output item of the reply, without its content. The assistant message comes first, atoutput_index 0, and each tool call follows in order, from 1.
response.text.delta
The next piece of the reply’s text, as Prism writes it.response.audio.delta
The next piece of the reply’s speech, with the same fields asresponse.text.delta. Here, delta holds base64 of headerless 24 kHz mono PCM16, little-endian, in whole samples. Deltas arrive faster than real time. The base64 value is shortened here:
response.function_call_arguments.done
A tool call for your backend to run, with all of its arguments. Prism sends no argument deltas. Tools lists the events of the full round.response.output_item.done
The complete item of a tool call. Prism sends it only for tool calls, right afterresponse.function_call_arguments.done. The content of the assistant message arrives in response.done.
response.text.done
The full text of the reply. Prism sends it beforeresponse.done for every reply, including cancelled, failed, and empty replies. A reply that only made tool calls and said no phrase has empty text.
response.audio.done
Prism finished sending the reply’s speech, and playback can still continue. Prism sends this event afterresponse.text.done, only for a reply that sent audio. A reply that only made tool calls and said no phrase does not send it. This event has the same fields as response.text.delta, without delta.
response.done
Ends a reply. Prism sends exactly one for eachresponse.created. It ends generation, not playback.
If Prism sent any of the reply’s audio, the message’s content part is
{"type": "audio", "transcript": ...}. If not, it is {"type": "text", "text": ...}. In both cases, the content part holds the reply text from response.text.done, including the spoken lines that Audio formats lists.
This reply to a spoken turn makes a tool call:
conversation.item.created
Acknowledgesconversation.item.create. It also acknowledges the caller’s item of a typed turn, before the response.created of that turn. It names the item, but does not repeat its text or output.
conversation.item.create example:
conversation.item.truncated
Acknowledgesconversation.item.truncate. It repeats the values of the report.
conversation.item.deleted
Acknowledgesconversation.item.delete, even if the item is already deleted. From the next reply on, the model does not see the item.
prism.conversation
Answersprism.conversation.get with the items of the conversation, in the order that the model sees them. The list also has any turns that truncation leaves out of the prompt.
Each item has only the fields that its row lists:
Prism lists each item from the moment that the last column shows, until you delete the item:
In this listing,
msg_root was added with previous_item_id "root", and msg_after_name with previous_item_id "msg_client_1":
error
Reports a refused request or a failure. Errors describes its fields.Errors
Anerror event tells you what went wrong and what it affects. Base your logic on code and scope, never on message. Errors and limits lists all 41 codes and what to do about each one.
These reply errors report a failure inside Prism and a conversation that is too long. Reply failures lists the other reply errors.
This error answers a
session.update that sets temperature:
Differences from OpenAI Realtime
Prism uses the event names of the OpenAI Realtime beta interface. An OpenAI Realtime client needs the changes below before it works with Prism. Move from OpenAI Realtime walks through them step by step.The current OpenAI interface
OpenAI’s current interface renamed some events and nested the session settings. Prism uses the beta names and the flat session, so map them back:
Client event names are the same in both interfaces. Prism refuses the nested session with
invalid_client_event, and the error names session.audio, session.type, or session.output_modalities. GPT-Live is a different OpenAI API with its own events, so these tables don’t map it.
Prism’s own events and fields have a prism prefix. The exceptions are session.idle_timeout, the session fields version and request_id, the usage field context_window, the error fields correlation_id, scope, and owner, and the reasons stop, disconnected, turn_failed, and response_timeout.
Limits
Errors and limits lists every limit and what happens when you pass it.

