# Conversation Relay: Build a Text-In, Text-Out Voice Bot on an Indian Number (2026)

> How Conversation Relay works: your bot exchanges text over a WebSocket while Edesy handles speech recognition, text-to-speech, barge-in and keypresses on an Indian phone number. Twilio ConversationRelay-compatible.

Source: https://edesy.in/blog/conversation-relay-text-voice-bot-india-2026

**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](https://voice-app.edesy.in/?utm_source=edesy.in&utm_medium=blog&utm_campaign=conversation-relay-text-voice-bot-india-2026). 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](https://edesy.in/blog/websocket-audio-streaming-voice-bot-2026).

## How a call flows

1. A caller dials your Edesy number.
2. Edesy opens a WebSocket to your bot and sends `setup` with `callSid`, `from`, `to`, `direction` and your
   `customParameters`.
3. If you set a `welcome_greeting`, Edesy speaks it. Otherwise your bot speaks first.
4. When the caller finishes speaking, your bot gets `prompt` with `voicePrompt` (the transcript).
5. Your bot streams back `text` tokens, with `last: true` on the final one.
6. Edesy speaks the reply. If the caller talks over it, your bot gets `interrupt`.
7. The bot ends the call with `end`, optionally with `handoffData`.

## 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

1. Run your bot. Edesy's Node sample bot listens on
   `ws://0.0.0.0:8787/relay`; expose it as `wss://` in production.
2. Preflight it:

```bash
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}'
```

3. 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/routing` with
   `inbound_target_type: "relay"` and `inbound_relay_url`.
4. 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](https://edesy.in/blog/migrate-twilio-calls-api-to-edesy-2026) 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](https://edesy.in/voice-api/pricing) for current rates.

More on the [Conversation Relay page](https://edesy.in/voice-api/conversation-relay), the [Voice API hub](https://edesy.in/voice-api), the
[call routing options](https://edesy.in/call-routing) and the [Voice API docs](https://edesy.in/docs/voice-api).

## Frequently Asked Questions

### What is Conversation Relay?

A way to put a text bot on a phone call. Edesy transcribes what the caller says and sends your bot text over a WebSocket; your bot sends text back and Edesy speaks it. Edesy also handles barge-in and keypresses, so your bot never touches audio.

### Is Edesy Conversation Relay compatible with Twilio ConversationRelay?

Yes. It uses Twilio's ConversationRelay protocol (setup, prompt, interrupt, dtmf, text, play, sendDigits, language, end), so a bot written for Twilio works unchanged when you point an Edesy number at it.

### Can the caller interrupt the bot?

Yes, by default. interruptible is on, and min_bargein_word_count (default 1) sets how many words the caller must say before the bot stops. Your bot receives an interrupt message with roughly what the caller heard.

### Can the bot switch language mid-call?

Yes. Send a language message with ttsLanguage and/or transcriptionLanguage. The default language on a new relay number is en-IN.

### How do I hand the call to a human?

Send an end message with handoffData. The call ends on the relay and the handoff data is recorded as a call.relay.handoff event that your system can act on.

### How do I test my bot without making a call?

Use POST /v1/relay-test with relay_url, a sample prompt and seconds. It connects from Edesy's network, runs one turn, and reports DNS, TCP, TLS, upgrade and protocol stages, including whether your reply set last: true.
