Blog

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:

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.