vox-rtc-server
Trusted Rust SDK for Vox-hosted WebRTC conversations. It creates sessions over HTTP and controls them over PondSocket.
PondSocket session
use ;
let client = new?;
let controlled = client.create_controlled_session.await?;
controlled.session.configure.await?;
Pass the API key in VoxRtcServerClientOptions or set VOX_API_KEY.
Browser WebSocket gateway
The SDK includes an Axum router compatible with
@eleven-am/vox-rtc-client:
use ;
let mut options = new;
options.api_key = var.ok;
options.path = "/api/vox/rtc".into;
let gateway = new?;
let listener = bind.await?;
serve.await?;
Each browser socket owns one controlled session. The socket remains open for SDP, full-trickle ICE, and lifecycle events while WebRTC carries media directly between the browser and Vox. Offer and candidate generations, including null end-of-candidates markers and stale generations, are forwarded unchanged. Legacy generation-less negotiation remains supported until a generated offer is received.
Call gateway.close("gateway_shutdown").await during application shutdown.
It signals active sockets, waits for their controlled sessions to close, and
then disconnects the shared Vox client. GatewayOptions also provides async
session-created/session-closed hooks and an error callback.
Speech context is opt-in and final-only. When enabled,
TranscriptEvent.speech_context contains a typed SpeechContext; otherwise it
is None. Schema v2 exposes timestamped emotions and vocal speaker spans
plus environmental sounds. Each sound also carries a score from 0 to 1.
use SpeechContextStatus;
if let Some = transcript.speech_context.as_ref
A partial result identifies the unavailable Speaker or Sounds track; a
failed result identifies both. Unsupported or malformed context is decoded as
None without dropping the transcript event.
Responses and generation correlation
Response senders accept an optional caller-chosen generation id via
ResponseOptions.generation_id; it is emitted as generation_id on
response.start, response.delta, response.commit, and response.cancel.
When omitted, the session generates one on start_response and threads it
through the follow-up commands automatically. Lifecycle events
(response.created|committed|done|cancelled, response.audio.clear,
interruption.*) expose the correlated generation_id when known.
Use start_response_and_wait to gate delta pumping on the start
acknowledgement instead of fire-and-forget:
use ;
use Duration;
use ;
let ack = controlled
.session
.start_response_and_wait
.await?;
if ack.accepted
response.created with the matching generation_id resolves the ack as
accepted; a typed error with the same generation_id resolves it as a
rejection carrying error_code, error_message, and recoverable.
The response-scoped output is optional. Vox fills omitted fields from the
session configuration and echoes the immutable effective selection in the
acknowledgement and ResponseEvent.
Error handling
error events are typed: code is a stable slug (see the
ERROR_CODE_* constants), recoverable says whether the session remains
usable, and generation_id scopes the failure to one response generation
when present.
Only recoverable == false (or the transport itself closing) is
call-ending — close and recreate the session. Every recoverable error is a
per-command failure: abort the affected generation if generation_id
matches, otherwise log and continue. Old Vox servers omit code and
recoverable; the SDK defaults a missing recoverable to true, so treat
those errors as recoverable unless the transport closed.
rtc.signaling_error is a separate WebRTC signaling failure surfaced by
on_signaling_error. It is terminal: Vox emits message and an optional
numeric generation, then closes the session. There is no recoverable
field — treat it as call-ending and recreate the session.