Skip to main content

Allow cookies so we can count page views, see which pages and campaigns bring people here, and measure our ads on Meta, Google and LinkedIn. Cookie policy

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 example https://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_412 as a placeholder.
  • A credential for the Authorization header, 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 Authorization header you just sent.
  • Idempotency — how to make a retried request safe, which matters most once you move from sessions to POST /v1/calls and 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.