Conversation Relay lets you run a voice bot that only handles text. When someone calls your Indian number, Edesy converts their speech to text and sends it to your bot over a WebSocket; your bot replies with text and Edesy speaks it, handling barge-in and keypresses. The protocol is Twilio ConversationRelay-compatible, so existing bots move over unchanged.
Put your bot on a number: Open the Edesy voice portal. Buy a number, choose the text-in, text-out bot target and paste your
wss://URL.
Why a text-only bot
Running speech recognition and text-to-speech yourself means picking vendors, handling audio formats, tuning voice activity detection and managing interruptions. That is real work. If what you want to build is the conversation itself (an LLM with your prompts and tools), Conversation Relay lets you skip the audio layer.
| Conversation Relay | Media Streams | |
|---|---|---|
| Your bot receives | Text transcripts | Raw L16 PCM audio |
| Your bot sends | Text (and play, sendDigits, end) | Audio |
| Who runs STT and TTS | Edesy | You |
| Barge-in | Edesy detects, tells your bot | You detect, send killAudio |
| Best for | LLM bots, fast builds, Twilio relay ports | Custom pipelines, Pipecat, specific voices |
For the audio route, see WebSocket audio streaming.
How a call flows
- A caller dials your Edesy number.
- Edesy opens a WebSocket to your bot and sends
setupwithcallSid,from,to,directionand yourcustomParameters. - If you set a
welcome_greeting, Edesy speaks it. Otherwise your bot speaks first. - When the caller finishes speaking, your bot gets
promptwithvoicePrompt(the transcript). - Your bot streams back
texttokens, withlast: trueon the final one. - Edesy speaks the reply. If the caller talks over it, your bot gets
interrupt. - The bot ends the call with
end, optionally withhandoffData.
Messages
| You receive | When |
|---|---|
setup |
Once, before anything is spoken |
prompt |
The caller finished an utterance (voicePrompt, lang) |
interrupt |
The caller talked over the bot (utteranceUntilInterrupt) |
dtmf |
A keypress, when DTMF detection is on |
error |
A platform-side problem; informational |
| You send | Effect |
|---|---|
text |
Speak it; stream tokens, last: true on the final chunk |
play |
Play an audio file, then continue |
sendDigits |
Send DTMF out of the call (w is a half-second wait) |
language |
Switch TTS and/or transcription language |
end |
End the call; handoffData is recorded |
Settings on the number
| Setting | Default | What it does |
|---|---|---|
welcome_greeting |
none | Spoken on connect |
| Voice | platform default | TTS voice ID |
| Language | en-IN |
TTS and transcription language |
interruptible |
on | Whether the caller can talk over the bot |
min_bargein_word_count |
1 | Words needed to interrupt; 0 means any sound, so a cough can stop the bot |
dtmf_detection |
off | Keypresses interrupt the bot and arrive as dtmf |
custom_parameters |
none | Passed verbatim in setup |
Put the tenant, campaign or store ID your bot needs in custom_parameters, so the bot doesn't have to look it up
by phone number.
Three things that catch people out
Set last: true. It flushes the speech engine. Without it, the end of each reply stays buffered and the bot
sounds like it swallowed its last sentence.
Handle interrupt. When the caller barges in, unplayed speech is discarded, so your history contains words
the caller never heard. Trim the last bot turn to utteranceUntilInterrupt, or the conversation drifts.
Accept the WebSocket upgrade quickly. The connection attempt is time-bounded, and a fallback message plays if it expires. Authenticate during the upgrade, not after.
Set it up
- Run your bot. Edesy's Node sample bot listens on
ws://0.0.0.0:8787/relay; expose it aswss://in production. - Preflight it:
curl -X POST https://voice-api.edesy.in/v1/relay-test \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{"relay_url":"wss://bot.example.com/relay","prompt":"what are your hours","seconds":5}'- In the portal, open your number, choose the text-in, text-out bot under "When a call comes in", paste the URL,
set the greeting and voice, and save. The API equivalent is
PATCH /v1/phone-numbers/:id/routingwithinbound_target_type: "relay"andinbound_relay_url. - Call the number.
What you can see after a call
The call timeline (GET /v1/calls/:sid/events) shows call.relay.connected, call.relay.failed (with a reason),
call.relay.handoff and call.relay.usage, which reports TTS characters, STT seconds, turns and interruptions.
Porting from Twilio
Point an Edesy number at the same WebSocket URL your Twilio ConversationRelay bot uses. The message types and fields are the same. What changes is everything around it: Indian numbers bought after KYC, a prepaid INR wallet with GST invoices, and a 60-second pulse with unanswered calls free. See the Twilio migration guide for the REST side.
Cost
Calls are billed at Rs 0.50 per minute on a 60-second pulse, unanswered calls free, plus number rental (Rs 499 per
month for mobile or 92-series). Prices exclude 18% GST. Speech usage for each call is reported in
call.relay.usage; see Voice API pricing for current rates.
More on the Conversation Relay page, the Voice API hub, the call routing options and the Voice API docs.