Eight things I learned shipping on Sarvam Voice Agents
Each of these cost me most of a day. Written down so they cost you an afternoon instead.
Last updated 13 September 2026
I built Svarloop on Sarvam's Voice Agents platform: a Windows desktop app that runs AI voice calling campaigns for Indian agencies and freelancers. It is in production, it speaks ten Indic languages plus English, and it uses five of Sarvam's API surfaces.
Sarvam's platform is good and their docs are decent. But there is a category of thing you only find out by shipping, and I lost a day to each of these. None of them are criticisms. They are the notes I wish someone had left me.
1. Five base paths, one host, and a silent 404
Voice Agents is not one API. It is five, all under apps.sarvam.ai, and picking
the wrong base is the most common way a new integration fails with nothing useful in the
response:
/api/scheduling/v1— campaigns and cohort uploads/api/outbounds/v1— a single call, placed now/api/analytics/v1— interactions, attempts, transcripts, recordings/api/app-authoring/v1— inbound deployments/api/user-context/v1— the workspace DND list
Scheduling paths also nest under your org and workspace:
/orgs/{org_id}/workspaces/{workspace_id}/campaigns. Both ids
come out of the console.
2. There are two kinds of key and they are not interchangeable
The Voice Agents platform key begins sk_samvaad_. The core speech key begins
sk_ and nothing else. They come from different screens, they authorise different
things, and using the speech key against the agents platform fails in a way that reads like a
permissions problem rather than a wrong key. If your calls will not place, check the prefix
before you check anything else.
3. Set retry_config.max_retries to 0
A campaign body takes a retry_config. Anything above zero pulls in an
intervals requirement that is not obvious from the shape of the request, and the
campaign is rejected. Zero is also the right default for outbound sales calling in India,
where you are already capped on attempts per number per day, so this is one of those rare
cases where the safe workaround is also the correct behaviour.
4. The cohort transformation file is not optional
Uploading a cohort is a multipart post with two files, not one. The CSV of leads, and a second file mapping which column holds the number:
{"phone_number": {"column_name": "mobile"}} Leave it out and the upload fails. There is no default, and the error does not point at the missing mapping.
5. Analytics never gives you a phone number back
This one reshapes your data model, so find it early. The analytics API returns
user_contact_masked, which is partial and display only, and
user_contact_hashed, which is stable. It never returns the raw number.
So you cannot join results back to your leads by phone. You join on your own hash of the
normalised E.164 number, or on a user_identifier you set when you build the
cohort. Decide which before you write anything, because retrofitting it is painful.
It also has a consequence worth designing for: an inbound call from someone who is not already in your database arrives with no dialable number at all. A masked string sitting in a phone field is worse than an empty one, because the rest of your app will assume it can be called.
6. is_debug_call is an integer
It is 1 for a test call and 0 otherwise, not a boolean. If it
arrives as the string "0", a truthy check marks every genuine call as a test,
and your call list comes back permanently and inexplicably empty. Compare against the integer:
int(value) == 1 Sarvam do flag this in their docs. I still lost a day to it.
7. A deployment with no inbound config answers around the clock
If you create an inbound deployment without start and end times, it answers 24 hours a day.
That is Sarvam's default and it is reasonable, but it is worth storing as null on your side
rather than as 00:00 to 23:59, so that "answers always" and "answers
a window that happens to cover the day" stay different things. You will want to tell them
apart later.
8. The DND list that actually stops a call is Sarvam's, not yours
This is the one with real consequences. Your own suppression list stops your app dialling someone. It does not stop Sarvam. Once a campaign is launched, Sarvam holds the cohort and Sarvam places the calls, and your local list is not consulted for any of it.
So somebody can ask on a call never to be contacted again, be honoured perfectly by every
check you wrote, and still be rung twenty minutes later by a campaign that was already in
flight. The fix is to push the suppression to the workspace DND list through
/api/user-context/v1, which Sarvam enforces across every outbound campaign.
Under TRAI this is an obligation, not a courtesy.
And one that is just good to know
Analytics is the source of truth. Webhooks and syncs are lossy, so reconcile a window against
analytics rather than trusting your own copy. limit maxes out at 1000 and
defaults to 20, and windows should overlap slightly or you will drop calls on the boundary.
If you are building on Voice Agents and hit something not on this list, I would like to know. svarloop@s8n.in.
Svarloop is free to install and use for finding businesses, building lists and rehearsing a pitch. Download it for Windows, or watch the minute long film first.