This page documents version 2.3.0 of the RTVI standard (Pipecat 1.15.0,
client-js 1.16.0). See Protocol Versions for what each
version changed.
Key Features
Connection Management
RTVI provides a flexible connection model that allows clients to connect to
AI services and coordinate state.
Transcriptions
The standard includes built-in support for real-time transcription of audio
streams.
Client-Server Messaging
The standard defines a messaging protocol for sending and receiving messages
between clients and servers, allowing for efficient communication of
requests and responses.
Advanced LLM Interactions
The standard supports advanced interactions with large language models
(LLMs), including context management, function call handling, and search
results.
Service-Specific Insights
RTVI supports events to provide insight into the input/output and state for
typical services that exist in speech-to-speech workflows.
Metrics and Monitoring
RTVI provides mechanisms for collecting metrics and monitoring the
performance of server-side services.
User Interface
RTVI lets a server-side agent observe and drive the clientβs GUI through
accessibility snapshots, UI events, and commands.
Terms
- Client: The front-end application or user interface that interacts with the RTVI server.
- Server: The backend service that runs the AI framework and processes requests from the client.
- User: The end user interacting with the client application.
- Bot: The AI interacting with the user, technically an amalgamation of a large language model (LLM) and a text-to-speech (TTS) service.
RTVI Message Format
The messages defined as part of the RTVI protocol adhere to the following format:string
A unique identifier for the message, used to correlate requests and responses.
string
default:"rtvi-ai"
required
A label that identifies this message as an RTVI message. This field is
required and should always be set to
'rtvi-ai'.string
required
The type of message being sent. This field is required and should be set to
one of the predefined RTVI message types listed below.
unknown
The payload of the message, which can be any data structure relevant to the
message type.
type, or a message whose data fails validation, it replies with an error-response carrying the messageβs id.
Protocol Versions
The client sends its protocol version inclient-ready, and the server sends its own in bot-ready. A server serves each client in the format of the clientβs version:
- A client on the serverβs major version is fully compatible.
- A client on any 1.x version is still served, with the 1.x
bot-outputformat. - Any other client version, or a missing or malformed one, gets an
error-responsewarning that compatibility issues may occur. The connection still proceeds.
bot-ready advertises a version that supports it.
RTVI Message Types
Following the above format, this section describes the various message types defined by the RTVI standard. Each message type has a specific purpose and structure, allowing for clear communication between clients and servers.Connection Management
client-ready π
Indicates that the client is ready to receive messages and interact with the server. Typically sent after the transport media channels have connected.- type:
'client-ready' - data:
-
version:
stringThe version of the RTVI standard being used. This is useful for ensuring compatibility between client and server implementations. -
about:
AboutClient ObjectAn object containing information about the client, such as its rtvi-version, client library, and any other relevant metadata. TheAboutClientobject follows this structure:
-
version:
bot-ready π€
Indicates that the bot is ready to receive messages and interact with the client. Typically sent after the transport media channels have connected.- type:
'bot-ready' - data:
-
version:
stringThe version of the RTVI standard being used. This is useful for ensuring compatibility between client and server implementations. -
about:
any(Optional) An object containing information about the server or bot. Its structure and value are both undefined by default. This provides flexibility to include any relevant metadata your client may need to know about the server at connection time, without any built-in security concerns. Please be mindful of the data you include here and any security concerns that may arise from exposing sensitive information. -
capabilities:
BotCapabilities Object(Optional)What the bot does in this session, so the client can show only the UI that applies, such as hiding video panels for a voice-only bot. Each field is optional:Introduced in RTVI version 2.2.0.truemeans the bot does it,falsemeans it doesnβt, and a missing field means unknown, in which case the client should keep its default behavior.
-
version:
disconnect-bot π
Indicates that the client wishes to disconnect from the bot. Typically used when the client is shutting down or no longer needs to interact with the bot. Note: Disconnects should happen automatically when either the client or bot disconnects from the transport, so this message is intended for the case where a client may want to remain connected to the transport but no longer wishes to interact with the bot.- type:
'disconnect-bot' - data:
undefined
error π€
Indicates an error occurred during bot initialization or runtime.- type:
'error' - data:
-
error:
stringDescription of the error. -
fatal:
booleanIndicates if the error is fatal to the session.
-
error:
system-log π€
Disabled by default; enable with
RTVIObserverParams(system_logs_enabled=True).- type:
'system-log' - data:
-
text:
stringThe log message.
-
text:
Speaking and Transcription
user-started-speaking π€
Emitted when the user begins speaking- type:
'user-started-speaking' - data: None
user-stopped-speaking π€
Emitted when the user stops speaking- type:
'user-stopped-speaking' - data: None
vad-user-started-speaking π€
Disabled by default; enable with
RTVIObserverParams(vad_user_speaking_enabled=True).user-started-speaking, which a turn strategy may gate or defer).
- type:
'vad-user-started-speaking' - data: None
vad-user-stopped-speaking π€
Disabled by default; enable with
RTVIObserverParams(vad_user_speaking_enabled=True).user-stopped-speaking, which a turn strategy may gate or defer).
- type:
'vad-user-stopped-speaking' - data: None
bot-started-speaking π€
Emitted when the bot begins speaking- type:
'bot-started-speaking' - data: None
bot-stopped-speaking π€
Emitted when the bot stops speaking- type:
'bot-stopped-speaking' - data: None
bot-interrupted π€
Introduced in Pipecat 1.4.0.
send-text with run_immediately). The client can drop whatever the bot was in the middle of saying. Sent when the serverβs bot_speaking_enabled option is on, as it is by default.
- type:
'bot-interrupted' - data: None
user-audio-level π€
Disabled by default; enable with
RTVIObserverParams(user_audio_level_enabled=True).- type:
'user-audio-level' - data:
-
value:
numberThe audio level.
-
value:
bot-audio-level π€
Disabled by default; enable with
RTVIObserverParams(bot_audio_level_enabled=True).- type:
'bot-audio-level' - data:
-
value:
numberThe audio level.
-
value:
user-mute-started π€
Introduced in RTVI version 1.2.0 (Pipecat 0.0.102, client-js 1.6.0).
- type:
'user-mute-started' - data: None
user-mute-stopped π€
Introduced in RTVI version 1.2.0 (Pipecat 0.0.102, client-js 1.6.0).
- type:
'user-mute-stopped' - data: None
user-transcription π€
Real-time transcription of user speech, including both partial and final results.- type:
'user-transcription' - data:
-
text:
stringThe transcribed text of the user. -
final:
booleanIndicates if this is a final transcription or a partial result. -
timestamp:
stringThe timestamp when the transcription was generated. -
user_id:
stringIdentifier for the user who spoke.
-
text:
user-input π€
What the user said or typed. The observer sends one for each transcription (before the matchinguser-transcription), for each user backchannel (a short acknowledgment such as βmhmβ said while the bot talks, which doesnβt interrupt the bot or reach the LLM), and as an acknowledgment of each send-text message.
- type:
'user-input' - data:
-
text:
stringWhat the user said or typed. -
input_type:
'transcription' | 'chat' | 'backchannel'The kind of input: speech transcribed by the STT, text the client sent, or a backchannel. -
timestamp:
stringThe timestamp when the user said or sent it. -
final:
booleanWhether the text is final. Only an interim transcription is not. -
user_id:
string(optional) Identifier for the user who spoke or typed. Forchat, only set when the transport knows the sender, such as Daily and LiveKit. -
msg_id:
string(optional) Forchat, the id of the clientβssend-textmessage. -
perception:
array(2.3.0+, optional) For a transcription, how the user said it, when the server reads it. Each item is a signal of a perception reading:-
name:
stringWhat is read, such as"tone". -
labels:
arrayFor a signal read as labels, its likeliest labels, likeliest first, each with alabel(string) and aconfidence(number) from 0 to 1. Empty for a signal read as a value. -
value:
number(optional) For a signal read as a value, the measure, such as a speech rate.
-
name:
-
text:
bot-output π€
The best-effort representation of the botβs output text, including both spoken and unspoken text. In addition to transcriptions of spoken text, this message type may also include text that the bot outputs but does not speak (e.g., text sent to the client for display purposes only). Along with the text, this event includes anoutput_type field to indicate what the text represents (e.g. βsentenceβ, βwordβ, βcodeβ, βurlβ) and fields describing whether and how far the bot has spoken it.
The botβs output is split into segments, such as sentences, each with its own segment_id. For clients of protocol 2.0.0 or later, a spoken segment is reported as it is spoken:
- If the TTS service reports word timestamps, the server sends the segment once with
spoken_status: "new"before it is spoken, then again as each word is spoken, withspoken_status: "in-progress"and the last with"completed". Every message carries the segmentβs fulltextand the samesegment_id. - Otherwise, the server sends the segment once, with
spoken_status: "completed", after it is spoken.
will_be_spoken: false and spoken_status: "new". Clients of protocol 2.0.0 or later donβt receive separate bot-output messages for individual words (output_type "word"); word-level progress is in spoken_progress instead. Clients on 1.x receive each spoken word as its own message and should read spoken.
- type:
'bot-output' - data:
-
text:
stringThe output text from the bot. -
output_type:
stringIndicates what kind of text this is (e.g., βsentenceβ, βwordβ, βcodeβ, βurlβ). βsentenceβ, βwordβ, and βbackchannelβ are reserved output types defined by the RTVI standard. Other output types may be defined by custom text aggregators used by the server. A short acknowledgment the bot says, such as βMm-hmm.β, has typebackchannel. It is only sent to clients of protocol 2.2.0 or later. A sound or pause the bot performs in expressive conversation has typeexpression_soundorexpression_pause. Itstextis the soundβs name, such as"laugh", or the pauseβs length in milliseconds, such as"600". Each is a segment of its own, sent once as itβs performed, withspoken_status: "completed". They are only sent to clients of protocol 2.3.0 or later. -
text_type:
string(deprecated) Deprecated. Useoutput_typeinstead, which carries the same value. Will be removed in 2.0.0. -
aggregated_by:
string(deprecated) Deprecated. Useoutput_typeinstead, which carries the same value. Will be removed in 2.0.0. -
segment_id:
number(optional) Identifies the segment this text belongs to, so a client can match a segmentβs"new","in-progress", and"completed"messages. -
will_be_spoken:
boolean(2.0.0+) Indicates if the TTS will speak this text. -
spoken_status:
'new' | 'in-progress' | 'completed'(2.0.0+) How far the bot has spoken the segment. Always'new'whenwill_be_spokenisfalse. -
spoken_progress:
object(2.0.0+, optional) How much of the segment has been spoken. Only present whenwill_be_spokenistrue.-
accumulated_text:
stringThe text already spoken, including the current word. -
remaining_text:
stringThe text not yet spoken.
-
accumulated_text:
-
delivery:
string[](2.3.0+, optional) In expressive conversation, the delivery of the text just spoken, by its universal names, such as["warm", "emphasis"]. Empty for the voiceβs baseline. It changes within a segment where the delivery does, so a segmentβs"new"message has none. -
spoken:
boolean(1.x) Indicates if this text was spoken by the bot. Clients of protocol 2.0.0 or later should readwill_be_spokenandspoken_statusinstead.
-
text:
bot-transcription π€
Transcription of the botβs speech. Note: This protocol currently does not match the user transcription format to support real-time timestamping for bot transcriptions. Rather, the event is typically sent for each sentence of the botβs response. This difference is currently due to limitations in TTS services which mostly do not support (or support well), accurate timing information. If/when this changes, this protocol may be updated to include the necessary timing information. For now, if you want to attempt real-time transcription to match your botβs speaking, you can try using thebot-tts-text message type.
- type:
'bot-transcription' - data:
-
text:
stringThe transcribed text from the bot, typically aggregated at a per-sentence level.
-
text:
Client-Server Messaging
server-message π€
An arbitrary message sent from the server to the client. This can be used for custom interactions or commands. This message may be coupled with theclient-message message type to handle responses from the client.
-
type:
'server-message' -
data:
anyThedatacan be any JSON-serializable object, formatted according to your own specifications.
client-message π
An arbitrary message sent from the client to the server. This can be used for custom interactions or commands. This message may be coupled with theserver-response message type to handle responses from the server.
-
type:
'client-message' -
data:
- t:
string - d:
unknown(optional)
tfield indicating the type of message and an optionaldfield containing any custom, corresponding data needed for the message. - t:
server-response π€
A message sent from the server to the client in response to aclient-message. IMPORTANT: The id should match the id of the original client-message to correlate the response with the request.
-
type:
'server-response' -
data:
- t:
string - d:
unknown(optional)
tfield indicating the type of message and an optionaldfield containing any custom, corresponding data needed for the message. - t:
error-response π€
Error response to a specific client message, such as aclient-message the application answers with an error, a message of an unknown type, or a message that failed validation. IMPORTANT: The id should match the id of the original client message to correlate the response with the request.
- type:
'error-response' - data:
-
error:
stringDescription of the error.
-
error:
Advanced LLM Interactions
send-text π
A message sent from the client to the server to send text input to the LLM, appended to the userβs context.- type:
'send-text' - data:
-
content:
stringThe text content to be appended to the user context. -
options:
object(optional)-
run_immediately:
boolean(optional) Iftrue, the pipeline should be interrupted and the LLM should process the input immediately after appending it to the context. Defaults totrue. -
audio_response:
boolean(optional) Iffalse, the bot should bypass the TTS so that the bot does not respond to the text in audio. Defaults totrue.
-
run_immediately:
-
content:
send-file π
Introduced in RTVI version 2.2.0 (Pipecat 1.13.0, client-js 1.14.0).
bot-ready message.
- type:
'send-file' - data:
-
content:
stringThe text to accompany the file. -
file:
object-
format:
stringThe fileβs MIME type, e.g.application/pdf. -
name:
string(optional) The fileβs name. -
source:
objectEither{ "type": "bytes", "bytes": "<base64>" }(images may also includewidthandheight), or{ "type": "url", "url": "..." }. The URL may be one returned by a file upload endpoint (e.g.pipecat:<id>from the Pipecat development runner), a cloud-storage URI (s3://,gs://), or a plainhttp(s)URL.
-
format:
-
options:
object(optional)-
run_immediately:
boolean(optional) Iftrue, the pipeline should be interrupted and the LLM should process the input immediately after appending it to the context. Defaults totrue. -
audio_response:
boolean(optional) Iffalse, the bot should bypass the TTS so that the bot does not respond to the file in audio. Defaults totrue. -
custom_options:
object(optional) Provider-specific options passed through with the file, e.g.detailfor OpenAI orcitationsfor Bedrock.
-
run_immediately:
-
content:
dtmf π
Introduced in Pipecat 1.5.0. RTVI version 2.1.0 (Pipecat 1.6.0, client-js
1.13.0) replaced the single
button field with buttons.- type:
'dtmf' - data:
-
buttons:
Array<string>One or more keypad entries:'0'-'9','*', or'#'. Must contain at least one entry.
-
buttons:
llm-function-call-started π€
Introduced in RTVI version 1.2.0 (Pipecat 0.0.102, client-js 1.6.0).
llm-function-call-in-progress and llm-function-call-stopped, is controlled by the serverβs function_call_report_level configuration, set per function:
- type:
'llm-function-call-started' - data:
-
function_name:
string(optional) Name of the function being called. Only included if the serverβs report level isNAME,ARGUMENTS, orFULL.
-
function_name:
llm-function-call-in-progress π€
Introduced in RTVI version 1.2.0 (Pipecat 0.0.102, client-js 1.6.0).
llm-function-call message and is the event that triggers registered FunctionCallHandlers when a function_name is present.
- type:
'llm-function-call-in-progress' - data:
-
function_name:
string(optional) Name of the function being called. Only included if the serverβs report level isNAME,ARGUMENTS, orFULL. -
tool_call_id:
stringUnique identifier for this function call. -
arguments:
Record<string, unknown>(optional) Arguments passed to the function. Only included if the serverβs report level isARGUMENTSorFULL.
-
function_name:
llm-function-call-stopped π€
Introduced in RTVI version 1.2.0 (Pipecat 0.0.102, client-js 1.6.0).
- type:
'llm-function-call-stopped' - data:
-
function_name:
string(optional) Name of the function that was called. Only included if the serverβs report level isNAME,ARGUMENTS, orFULL. -
tool_call_id:
stringIdentifier matching the original function call. -
cancelled:
booleanIndicates whether the function call was cancelled before completing. -
result:
unknown(optional) The result of the function call, if available. Only included if the serverβs report level isFULL.
-
function_name:
llm-function-call π€
A function call request from the LLM, sent from the bot to the client. Note that for most cases, an LLM function call will be handled completely server-side. However, in the event that the call requires input from the client or the client needs to be aware of the function call, this message/response schema is required.- type:
'llm-function-call' - data:
-
function_name:
stringName of the function to be called. -
tool_call_id:
stringUnique identifier for this function call. -
args:
Record<string, unknown>Arguments to be passed to the function.
-
function_name:
llm-function-call-result π
The result of the function call requested by the LLM, returned from the client.- type:
'llm-function-call-result' - data:
-
function_name:
stringName of the called function. -
tool_call_id:
stringIdentifier matching the original function call. -
arguments:
Record<string, unknown>Arguments that were passed to the function. -
result:
Record<string, unknown> | stringThe result returned by the function.
-
function_name:
bot-llm-search-response π€
Search results from the LLMβs knowledge base.Currently, Google Gemini is the only LLM that supports built-in search.
However, we expect other LLMs to follow suit, which is why this message type
is defined as part of the RTVI standard. As more LLMs add support for this
feature, the format of this message type may evolve to accommodate
discrepancies.
- type:
'bot-llm-search-response' - data:
-
search_result:
string(optional) Raw search result text. -
rendered_content:
string(optional) Formatted version of the search results. -
origins:
Array<Origin Object>Source information and confidence scores for search results. TheOrigin Objectfollows this structure:
-
search_result:
Service-Specific Insights
stt-raw-text π€
Each transcription, including interim ones, as the STT produced it. Sent to clients of protocol 2.2.0 or later, when enabled on the server.- type:
'stt-raw-text' - data:
-
text:
stringThe transcribed text. -
final:
booleanIndicates if this is a final transcription or a partial result. -
timestamp:
stringThe timestamp when the transcription was generated. -
user_id:
stringIdentifier for the user who spoke.
-
text:
bot-llm-started π€
Indicates LLM processing has begun- type:
bot-llm-started - data: None
bot-llm-stopped π€
Indicates LLM processing has completed- type:
bot-llm-stopped - data: None
user-llm-text π€
Aggregated user input text that is sent to the LLM.- type:
'user-llm-text' - data:
-
text:
stringThe userβs input text to be processed by the LLM.
-
text:
bot-llm-text π€
Individual tokens streamed from the LLM as they are generated.- type:
'bot-llm-text' - data:
-
text:
stringThe token text from the LLM.
-
text:
llm-raw-text π€
Each chunk of text the LLM streams. Sent to clients of protocol 2.2.0 or later, when enabled on the server.- type:
'llm-raw-text' - data:
-
text:
stringThe text chunk from the LLM.
-
text:
bot-llm-marker π€
Introduced in Pipecat 1.11.0. Disabled by default; enable with
RTVIObserverParams(bot_llm_marker_enabled=True).FilterIncompleteUserTurnStrategies. Sent once per response, when it ends. Because it carries the raw text of the whole response, itβs meant for evaluation and debugging rather than for end-user clients.
- type:
'bot-llm-marker' - data:
-
text:
stringThe marker as the LLM produced it; empty when the response carried none. -
kind:
string(optional) What the marker means, in the emitterβs vocabulary (e.g.complete,short, orlongfor turn completion). -
raw:
stringThe responseβs text as the LLM produced it, markers included. -
markers:
Array<string>Every marker the emitter recognizes.
-
text:
bot-tts-started π€
Indicates text-to-speech (TTS) processing has begun.- type:
'bot-tts-started' - data: None
bot-tts-stopped π€
Indicates text-to-speech (TTS) processing has completed.- type:
'bot-tts-stopped' - data: None
bot-tts-text π€
The per-token text output of the text-to-speech (TTS) service (what the TTS actually says).- type:
'bot-tts-text' - data:
-
text:
stringThe text representation of the generated bot speech.
-
text:
tts-raw-text π€
The text the TTS service speaks, as it is spoken. Sent to clients of protocol 2.2.0 or later, when enabled on the server.- type:
'tts-raw-text' - data:
-
text:
stringThe text being spoken.
-
text:
Metrics and Monitoring
metrics π€
Performance metrics for various processing stages and services. Each message will contain entries for one or more of the metrics types below.- type:
'metrics' - data:
- processing: [See Below] (optional) Processing time metrics.
- ttfb: [See Below] (optional) Time to first byte metrics.
-
ttfa:
Array<object>(optional) TTS time to first audio: time to first byte plus any leading silence. Instead ofvalue, each entry hasttfa,ttfb, andleading_silence, in seconds. -
ttfat:
Array<object>(optional) LLM time to first answer token: time to first byte plus any reasoning the model streamed first. Instead ofvalue, each entry hasttfat,ttfb, andthinking_time, in seconds. - characters: [See Below] (optional) TTS character usage metrics.
-
tokens:
Array<object>(optional) LLM token usage. Each entry holds the token counts reported by the LLM service, such asprompt_tokens,completion_tokens, andtotal_tokens, withoutprocessorormodel. -
stt_usage: [See Below] (optional)
STT usage metrics, where
valueis an object withaudio_seconds: the seconds of audio sent to the service since the previous report.
processing, ttfb, characters, and stt_usage, the data structure is an array of objects with the following structure:
-
processor:
stringThe name of the processor or service that generated the metric. -
value:
numberThe value of the metric: seconds forprocessingandttfb, a character count forcharacters. -
model:
string(optional) The model of the service that generated the metric, if applicable.
User Interface
These messages let a server-side agent observe and drive a GUI on the client. The client streams the screen as accessibility snapshots and dispatches user events; the agent acts on the page with commands and surfaces long-running work as job-group cards. On the server,UIWorker builds higher-level abstractions on top of these messages, but single-LLM apps can target the wire format directly. On the client, see the JavaScript client methods and React hooks.
ui-snapshot π
A serialized accessibility tree describing what is currently on screen. Sent whenever the page changes so the agent always reasons over the latest state.- type:
'ui-snapshot' - data:
-
tree:
A11ySnapshot ObjectThe accessibility snapshot. The agent renders it into a<ui_state>block for its LLM. TheA11ySnapshotobject follows this structure: Extra fields are allowed on the snapshot models so clients can add platform-specific or future metadata without breaking older servers.
-
tree:
ui-event π
An app-defined event dispatched by the client (e.g. a button click, a custom interaction). On the server these route to@ui_event(name) handlers.
- type:
'ui-event' - data:
-
event:
stringThe app-defined event name. -
payload:
any(optional) App-defined payload, schemaless by design.
-
event:
ui-cancel-job-group π
Requests cancellation of an in-flight job group the agent started (seeui-job-group). Honored only when the group was started as cancellable.
- type:
'ui-cancel-job-group' - data:
-
job_id:
stringThe job-group id the client wants cancelled. -
reason:
string(optional) A human-readable reason.
-
job_id:
ui-command π€
A command from the agent for the client to act on the page. Client-side handlers dispatch on the command name;@pipecat-ai/client-react ships default handlers for the standard commands below.
- type:
'ui-command' - data:
-
command:
stringThe command name (e.g."scroll_to","toast", or any app-specific name). -
payload:
any(optional) The command payload. Standard command payloads:
-
command:
ui-job-group π€
Lifecycle envelopes for a user-facing job group the agent dispatched to peer workers, so the client can render a progress card. Each envelope carries akind field the client dispatches on. The kinds form the groupβs lifecycle:
-
type:
'ui-job-group' -
data: one of the four
kind-discriminated shapes: