How do I build a voice agent with VARTA?
Building a voice agent with VARTA is three calls: POST /v1/agents/{agent_id}/load opens a session against an agent you built in the
studio, POST /v1/sessions/{session_id}/turn sends the caller's transcribed
text and gets back the agent's reply, and POST /v1/sessions/{session_id}/end
closes the session and finalises its cost. Everything else — speech
recognition, speech synthesis, the conversation logic, cost tracking — VARTA
does for you between those calls.
Prerequisites
- A reachable VARTA instance and its base URL. Every example below uses
$VARTA_BASE_URL, which should already include the version prefix — for examplehttps://your-varta-host/v1. - An agent id from the VARTA studio. An agent bundles a language model, a
speech recogniser, a speech synthesiser, a voice and a conversation flow
under one id; you build it in the studio, then load it from your code. This
page uses
ag_412as a placeholder. - A credential for the
Authorizationheader, if the instance you're talking to runs in enforce mode. See Authentication before you point this at anything other than your own machine — a default self-hosted instance does not require one, and that is not something to rely on past your own network.
Everything here runs against your own VARTA instance — there is no shared VARTA Cloud endpoint to call yet. VARTA Cloud will be the same API at a different host; it isn't open yet, and the waitlist is the way in when it is.
1. Load the agent
curl -X POST "$VARTA_BASE_URL/agents/ag_412/load" \
-H "Authorization: Bearer $VARTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"customer_name": "Rahul", "context": {"order_id": "SO-4821"}}'Every field in the body is optional. The response carries the opening line with its audio already synthesised, so your client never has to assemble a greeting itself:
{
"session_id": "se_9021",
"agent_id": "ag_412",
"opening": {
"text": "Namaste Rahul, main Priya baat kar rahi hoon...",
"audio_url": "https://.../opening_9021.mp3",
"clips": [ { "intent_key": "opening_greeting", "text": "Namaste Rahul, main Priya baat kar rahi hoon...", "audio_url": "https://.../opening_9021.mp3" } ]
},
"filler_clips": {},
"behavior_clips": {},
"runtime": { "agent_name": "Priya", "language": "hi", "steps": 9 },
"ended": false
}The field to hold on to is session_id — every following call is scoped to
it. runtime tells you which model, speech recogniser and speech
synthesiser this session actually resolved to (an agent can pin a provider or
fall back to the deployment default; see Agents for the
full shape). Play opening.audio_url to the caller and you're already
talking.
2. Submit a turn
Once you have the caller's words as text — from your own speech recogniser, or from a browser's built-in transcription — send it in:
curl -X POST "$VARTA_BASE_URL/sessions/se_9021/turn" \
-H "Authorization: Bearer $VARTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "haan boliye", "stt_confidence": 0.94, "stt_provider": "deepgram"}'{
"session_id": "se_9021",
"text": "Aapka order kal deliver ho jayega.",
"audio_url": "https://.../turn_4.mp3",
"cache_hit": true,
"ended": false,
"trace": { "layer": "l1_exact_match", "llm_used": false, "latency_ms": { "total": 210 } },
"cost": { "llm_inr": 0, "tts_inr": 0, "stt_inr": 0, "total_inr": 0 },
"session_totals": { "total_inr": 0.05, "turns": 4, "cache_hit_percent": 75 }
}Three fields are worth reading on every turn, not just the reply text:
trace.layer names which layer of the engine decided the reply (a keyword
match, a rule, the LLM — useful for debugging why the agent said what it
said, not a stable enum to branch on), cache_hit tells you whether the
audio came free from the pre-synthesis cache or a live text-to-speech call,
and cost / session_totals give you the turn's spend and the session's
running total. Call this endpoint again for every subsequent thing the
caller says — see Sessions for the full
response shape, including the flags that show up on filler and
confirmation-readback clips.
3. End the session
curl -X POST "$VARTA_BASE_URL/sessions/se_9021/end" \
-H "Authorization: Bearer $VARTA_API_KEY"{
"session_id": "se_9021",
"ended": true,
"summary": { "...": "structured end-of-call summary" },
"cost": { "llm_inr": 0.04, "tts_inr": 0.01, "stt_inr": 0, "total_inr": 0.05 }
}Costs stop accruing once the session is ended, and summary — step
outcomes, tool calls, overall outcome, and whatever the instance's summariser
extracted — becomes available. It is a generated summary, not a structured slot
map: state.slots on each turn is always empty, as
Sessions explains.
Building that summary is best-effort and never fails the request, but the
failure is coerced to an empty object, not a null:
(result or {}).get("summary") or {}
(backend/app/api/v1/sessions.py:97). So summary is never null — check
for {} instead, which means the summary build failed or produced nothing.
Next steps
- Authentication — whether the instance you're
calling actually enforces the
Authorizationheader you just sent. - Idempotency — how to make a retried request safe,
which matters most once you move from sessions to
POST /v1/callsand a retry could ring the phone twice. - Examples — worked, runnable versions of this flow.
- Not yet in v1 — what the API does not cover today (authoring agents), and where that's headed.