Blog

The opt out that did not stop the call

We honoured every request perfectly. The call went out anyway. Found 8 September 2026.

Last updated 13 September 2026

← All posts

Svarloop places outbound calls for Indian agencies, so the first thing we built was the part that stops a call. Three checks run before any number is dialled: the TRAI window of 9 AM to 9 PM, a cap of three attempts per number per day, and a do not contact list. If someone says on a call that they do not want to be contacted again, it is picked up from what they actually said and written to that list automatically.

We tested it. Add a number, try to call it, refused. Say the words on a live call, watch the row appear, try again, refused. It worked.

It also did nothing.

What we missed

Our checks run when Svarloop places a call. But a campaign is not a series of calls Svarloop places. You build a cohort, hand it to Sarvam, and Sarvam dials it. From the moment that upload succeeds, the list lives on their side and every call comes off their copy.

Our do not contact list is never consulted for any of it. It cannot be. It is on a laptop in Pune and the dialling is happening in a data centre.

So the failure looks like this. Someone picks up, says please do not call me again, and means it. The transcript follow up catches it. The number is written to the local list within seconds. Every check we wrote honours it perfectly, forever. And twenty minutes later their phone rings again, because a campaign launched an hour ago is still working through a cohort that was copied before any of that happened.

That is the exact thing do not call rules exist to prevent. Under TRAI it is an obligation, not a courtesy. And we had built a system that logged its own compliance impeccably while the calls went out regardless.

The fix, and why it is the only one

Sarvam has a workspace level DND list, at /api/user-context/v1, and they enforce it across every outbound campaign in the org with no per campaign setup. Putting the number there is the only thing that stops a call already in flight, because it is the only list that exists on the side doing the dialling.

So an opt out now goes to both places. Local first, always, then pushed to Sarvam. If Sarvam is unreachable the number is still suppressed locally and pushed on a later pass, because losing the local record over a failed network call would be much the worse of the two failures.

The part that was harder than the API call

Our suppression table stores a SHA-256 of the number and never the number itself. That was a deliberate decision made long before this, and the whole product is sold on not holding people's data, so undoing it to make an integration easier was not on the table.

Sarvam's API, reasonably, wants the real E.164 number.

The resolution has three parts. New opt outs are pushed at the moment of suppression, while the number is still in hand and before it is hashed away. For entries already in the table with only a hash, the raw number can often be recovered by hashing the customer's own leads and looking for a match, which covers every opt out that came from a call, and those are all of the automatic ones. And anything still unmatched is reported to the customer as unrecoverable rather than quietly skipped, because a compliance tool that fails silently is worse than one that fails.

That last one matters more than it sounds. The tempting version of this feature returns "done" and moves on. The honest version tells you that four numbers could not be pushed and you should handle them yourself.

What we took from it

A check that runs on your side of an API boundary governs your side of it and nothing else. We knew that abstractly. We still wrote three careful compliance checks, tested all of them, and shipped a product where a person could ask to be left alone, be honoured perfectly, and get called anyway.

If you are building on any voice platform, the question worth asking early is not "does my suppression list work". It is "who is actually holding the list at the moment the number is dialled". If the answer is not you, your list is a record, not a control.


This is one of a series of notes from building Svarloop on Sarvam's Voice Agents platform. The others are on the blog, including eight things the docs do not tell you.