Provider-independent realtime voice agents
Ship the voice agent once. Change the models underneath it without touching your app
Your backend creates a session, the browser joins a room. Speech-to-text, the language model, the voice, or a full realtime engine all live in a published profile. Swap vendors there and your shipped code keeps working.
One session contract, protocol v1 · STT, LLM, TTS and realtime engines from 10 providers · SDKs for the browser, Node and Python, all optional
Pick a provider per stage. Connect your own keys.
How a session runs
Publish once. Create from the backend. Connect from the browser. Read everything back.
A profile is the only place vendors appear. Your application only ever sees the session contract: one POST to create, one room to join, one document to read when the call ends.
- 1
Publish a profile
Choose the STT, LLM and TTS stages, or a realtime engine. Attach tools, set turn detection and interruption rules. Publishing freezes an immutable revision.
rev 12 - 2
Issue a key
An organisation API key with only the scopes it needs. Test keys for staging, live keys for production. It stays on your server.
sk_live_… - 3
Backend creates the session
One POST with the profile name, your record id and an idempotency key. You get a session id, short-lived room credentials and a browser-safe access token.
POST /v1/sessions - 4
Browser joins the room
The client SDK publishes the microphone, plays the agent, orders events, replays gaps and rejoins after a network drop. Browser tools run right there.
connect() - 5
Read the session back
Transcript, audio duration, usage per vendor, interruptions and tool calls in one bounded document, and a signed lifecycle webhook when the call ends.
GET …/data
Features
The session contract stays still. The stack underneath it moves.
Voice quality, latency and price change every quarter as providers ship new models. VoxEvolv keeps that churn inside the profile, so evaluating a new model is a publish, not a migration.
Profiles hold every vendor decision, and every published revision is immutable
A profile names the engine for each stage, the voice, the language, turn detection, interruption behaviour, and the tools the model may call. Publishing produces a numbered revision that never changes. Sessions record the revision they ran on, so a transcript from March is explained by the exact configuration that produced it.
- Cascaded or realtime. Run Sarvam STT into Claude into ElevenLabs, or switch the whole profile to OpenAI Realtime or Gemini Live. The session contract is the same either way.
- Your provider keys. Connect each vendor once, encrypted at rest. Profiles reference the connection, never the secret.
- Conversation controls per profile. Voice activity detection, endpointing, barge-in, greeting, and how long silence may run before the agent speaks.
- Test before publishing. A secure test link opens the draft in a browser. When it sounds right, publish.
Agent profile · support-agent
Draft on top of revision 12 · 1 stage changedsupport-agent runs on revision 13.One session contract from your backend, one connect call from the browser
The organisation key never leaves your server. Your backend posts the profile name, your own record id, and the participant. The response carries everything the browser needs and nothing it should not have: short-lived room credentials and a scoped access token that can rejoin, replay and end only its own session.
- Idempotent by design. One
Idempotency-Keyper intended call. Retry safely; a replay returns the same session. - SDKs are optional.
@voxevolv/server,@voxevolv/clientand the Python package wrap a documented HTTP and protocol v1 surface you may call directly. - Recovery is built in. The client distinguishes transport reconnects from a full session resume, replays missed events, and preserves mute state across a rejoin.
- Migrating from OpenAI Realtime or Gemini Live? A machine-readable guide at
/realtime-migration.txtwalks a coding agent through the swap.
Create a session, then connect
Backend · then browser# trusted backend only curl -X POST "$VOXEVOLV_API_BASE/sessions/" \ -H "Authorization: Bearer $VOXEVOLV_API_KEY" \ -H "Idempotency-Key: order-4412-call-1" \ -d '{ "protocol_version": 1, "client_reference_id": "order-4412", "profile": "support-agent", "participant": { "identity": "user-4412" }, "media": { "input": ["audio"], "output": ["audio", "text"] } }' # 201 Created { "id": "9f2c…", "profile_revision": 12, "connection": { "url", "token", "room_name" }, "access_token": "vsa_…" }
// browser const session = await RealtimeSession.create({ endpoint: "/api/voice-session", autoRejoin: true, tools: { show_order: async ({ order_id }) => fetchOrder(order_id) }, }); session.onAgentTranscript(({ text, final }) => final && log(text)); await session.connect({ microphone: true });
vsa_… token scoped to this one session.Tools the model can call, in the browser or on your server, discovered on demand
Register a tool once, attach it to a profile, publish. Browser tools run as RPC inside the page the user is looking at. Webhook tools call your server with a signed request. Groups of tools can stay dormant until the model searches for them, so a 40-tool agent starts with 6 in context.
- Browser tools.
session.registerTool(name, handler). The model's validated arguments arrive in the page; whatever you return goes back to the model. - Webhook tools. A JSON POST with session id, call id and arguments, signed with HMAC-SHA256 and deduplicated with an idempotency key.
- Inline tools per session. Pass up to 32 client tool definitions at creation time when a profile allows it, for tools that only exist on one screen.
- Platform tools. Attach
voxevolv.end_calland the model can close the conversation itself, draining its farewell before the room closes.
Tool manifest · support-agent rev 12
Captured at session start · 2 active · 2 on demandAlways active
On demand · group "payments"
Platform
- 00:41✓show_order("4412")Browser returned the order in 80 ms · agent read out the delivery date
- 02:14?search_tools("refund")Activated group "payments" · persisted so a worker restart keeps it
- 02:31✓initiate_refund(order: "4412", amount: 1499)Webhook 200 in 410 ms · signature verified by your server
- 03:05✓voxevolv.end_callFarewell drained, room closed, lifecycle webhook queued
See where the milliseconds went, and what each vendor billed, per session
A voice agent lives or dies on the gap between the user stopping and the agent starting. VoxEvolv records that gap for every turn and splits it by stage, so when a provider slows down you see which one. Usage is metered per vendor and per model, in the units each one bills, tokens or speech seconds.
- Stage timeline per turn. Speech end, transcript final, first token, first audio byte, all timestamped.
- One bounded session document.
GET /v1/sessions/{id}/datareturns the conversation, audio duration, usage, interruptions, tool calls and health in one response, with a paginated link for long transcripts. - Lifecycle webhooks. The same document is delivered to your endpoint when the session ends, signed, with an event id for deduplication.
- Debug capture, opt in. Keep the exact model inputs and outputs, including images sent as context, for a bounded window when you are chasing a bad answer.
Session order-4412 · turn 3 of 9
support-agent rev 12 · Hindi · Sarvam → Claude → ElevenLabsWho uses it
Teams that want voice in the product without marrying a model vendor
Product teams adding voice
Support, ordering, onboarding, in-app assistantsShip the voice feature against the session contract and keep shipping. When a faster or cheaper model appears, the change is a profile revision that the product team never has to schedule.
Training and assessment platforms
Role plays, interviews, coaching, certificationRun thousands of concurrent spoken conversations in Indian languages and English, with interruption handling that survives a noisy shop floor, and read every transcript back for scoring.
Contact centre and CX builders
Inbound, outbound, verification, collectionsChoose Sarvam for Hindi and Kannada speech, a frontier model for reasoning and a low-latency voice for the reply, and keep per-vendor usage separated for the finance team.
Coding agents and integrators
Build from llms.txt and the live OpenAPI documentMachine-readable instructions at /llms.txt, a migration guide from OpenAI Realtime and Gemini Live, and a live Swagger reference mean an agent can build a working integration from an API key and a profile name.
Trust
Secrets stay on the server. Credentials are short-lived and scoped.
Organisation API keys carry only the scopes you grant. Provider keys are encrypted at rest and never leave the worker. The browser gets a single-room credential and a token that can act on one session. Published revisions are immutable, and nothing is deleted, only disabled, so the record stays whole.
Bring one system prompt and one provider key
Create an account, connect a provider you already pay for, publish a profile and talk to it from a test link. Then change the model and publish again. The whole loop is self-serve and takes less time than reading a vendor's migration guide.
Your first half hour
- 1Create an account and organisation
/signup - 2Connect a providerAnthropic, OpenAI, Google, Sarvam, ElevenLabs or another
- 3Publish an agent profilepick the stages, set the voice, attach a tool
- 4Talk to it from a test linkthen swap the LLM stage and publish revision 2
- 5Create a key and call the API
POST /v1/sessions/from your backend