Version 1
Protocol reference
One REST call sets a conversation up. One WebSocket carries the audio. Everything else is detail.
Base URL · https://api.developers.dvaarik.com
How it works
You keep your telephony. Your provider hands you live call audio — Twilio, Plivo and Exotel all call this a media stream, and a SIP trunk behind a gateway like Jambonz or FreeSWITCH does the same thing. Your server bridges that audio to our socket and plays back what returns. Browser and app audio work identically.
Create a call
Authenticate with the X-Api-Key header. The response carries a WebSocket URL that is single-use and expires in five minutes.
POST /v1/calls
X-Api-Key: dvk_live_…
{
"prompt": "You are Riya, the receptionist at Sunrise Dental…",
"language": "en-IN",
"grade": "standard",
"voice": "en-IN-AartiIndicNeural",
"sample_rate_in": 8000,
"sample_rate_out": 8000
}Your system prompt, up to 32,000 characters. Held in memory for the life of the call and never written to disk.
One of eleven Indian locales. Defaults to en-IN. Call /v1/voices for the list.
essential ₹1.50, standard ₹2, studio_hd ₹3 or premium ₹3.75 per minute. Grades differ in which languages they can speak — a mismatch is refused with a 400 naming a grade that can. The rate is fixed when the call is created.
Must belong to the grade. Defaults to that grade’s house voice.
How the agent should open. Leave it out for a plain greeting.
Your audio to us: 8000 or 16000 Hz. Phone lines are 8000.
Our audio to you: 8000, 16000 or 24000 Hz. Match your playback path.
Up to 2 KB of your own JSON, returned unchanged on the call record.
A 402 means your balance cannot cover one full-length call. Top up and retry.
Stream audio
Connect to the returned ws_url. Send raw PCM16 mono binary frames at your chosen input rate — 20 to 100 ms per frame works well. Keep sending during silence: the endpointer needs it to know a turn has finished.
import asyncio, json, websockets, httpx
r = httpx.post("https://api.developers.dvaarik.com/v1/calls",
headers={"X-Api-Key": KEY},
json={"prompt": "You are a friendly receptionist."})
ws_url = r.json()["ws_url"]
async with websockets.connect(ws_url) as ws:
async for frame in ws:
if isinstance(frame, bytes):
play(frame) # PCM16 out
else:
handle(json.loads(frame))Events
Binary frames are audio. Text frames are JSON events on the same socket.
The session is live. Start sending audio.
What was said, with role: user | assistant. Streamed to you and never stored by us.
The caller spoke over the agent. Drop your playback buffer at once, or they will keep hearing a sentence the agent has already abandoned.
The agent finished speaking.
The call is over, with a reason. The socket closes next.
Close codes name their own refusal: 4401 bad or expired token, 4404 unknown call, 4408 the setup expired, 4429 your tier’s concurrent-call limit, 4409 that call was already connected.
Billing
Prepaid balance, charged when a call ends. Minutes are rounded up: a 61-second standard call bills two minutes, ₹4. The exact seconds stay on the call record. Calls cap at ten minutes. When the balance runs out, new calls are refused — a call already in progress is never cut off.
Endpoint reference
Create a call and receive its WebSocket URL.
The audio socket. Token comes from the create response.
Your recent calls with durations and costs.
One call record.
Balance, tier and concurrency allowance.
Grades, prices, voices and languages. No key needed.
Notes for production
- Barge-in is yours to honour. The
interruptedevent only helps if your playback path can drop its buffer immediately. - Resample before you send. We accept 8 kHz and 16 kHz. Downsample 44.1 or 48 kHz mic audio on your side.
- Tools are not in v1. The agent converses and hangs up. Webhook tool calls are on the roadmap — tell us what you need them for.
- Your data stays yours. We store durations, rates and costs. Not prompts, not transcripts.
Ready to place a call?
Create an account, add ₹500, and make your first call today.