> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inworld.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Telephony (Twilio)

> Power phone calls with Realtime TTS using Twilio ConversationRelay and the LLM + TTS Router feature

Twilio [ConversationRelay](https://www.twilio.com/docs/voice/twiml/connect/conversationrelay) handles call audio and speech-to-text for you and streams user transcripts to your server over a WebSocket. Your server then calls the Realtime Router API with the [LLM + TTS](/router/guides/llm-plus-tts) feature, which returns the LLM response and synthesized audio in a single streaming request. The server hands the audio back to ConversationRelay for playback.

<Note>
  This integration relies on the Realtime Router API's LLM + TTS streaming response. Because LLM inference and TTS synthesis happen in one call, the first sentence of audio starts playing before the LLM has finished writing the full response, resulting in improved latency.
</Note>

## Prerequisites

* [Node.js](https://nodejs.org/) v20 or later
* [ngrok](https://ngrok.com/) account with a reserved static domain (the free tier is sufficient)
* [Twilio](https://www.twilio.com/) account with a phone number that has Voice capability.
* [Inworld](https://www.inworld.ai/) account with a Router API key

## Setup

The steps below walk through the reference implementation in [inworld-ai/inworld-api-examples](https://github.com/inworld-ai/inworld-api-examples/tree/main/integrations/twilio-conversation-relay).

### 1. Clone the example repo

```bash theme={"system"}
git clone https://github.com/inworld-ai/inworld-api-examples.git
cd inworld-api-examples/integrations/twilio-conversation-relay
```

The remaining steps run from this directory.

### 2. Get your Inworld API key

Sign in to the [Inworld Portal](https://portal.inworld.ai/), open your workspace, and create an API key. The same key works for Router (LLM) and TTS because the integration uses the combined LLM + TTS endpoint.

### 3. Get a Twilio phone number

In the [Twilio Console](https://console.twilio.com/), buy a phone number with Voice capability. This is the number callers will dial.

### 4. Reserve an ngrok static domain (for local development)

Install ngrok and reserve a free static domain in the [ngrok dashboard](https://dashboard.ngrok.com/domains). A static domain matters here because Twilio's webhook URL needs to stay stable between restarts. Without one, every new ngrok session changes the tunnel URL and you have to update the Twilio webhook by hand.

### 5. Configure environment

Copy the example env file and fill in the required variables:

```bash theme={"system"}
cp .env.example .env
```

At minimum, set:

```bash theme={"system"}
INWORLD_API_KEY=your_inworld_api_key
SERVER_URL=https://your-ngrok-domain.ngrok-free.app
```

Optional variables (with sensible defaults) let you change the system prompt, LLM model, TTS voice and model, STT provider, and welcome greeting. See the repo README for the full table.

### 6. Install and run

Install dependencies:

```bash theme={"system"}
npm install
```

Then start the dev server and ngrok in two separate terminals:

```bash theme={"system"}
npm run dev
```

```bash theme={"system"}
ngrok http 3000 --url=your-ngrok-domain.ngrok-free.app
```

### 7. Point your Twilio number at the webhook

In the Twilio Console, go to **Phone Numbers** → **Manage** → **Active Numbers** → your number. Under **Voice Configuration**, set **A call comes in** to **Webhook**, enter `https://your-ngrok-domain.ngrok-free.app/voice`, and choose **HTTP POST**. Save the configuration.

<Note>
  ngrok is only needed for local development so Twilio can reach a server running on your machine. Once you deploy the server to production, update the Twilio webhook to point at your server's public URL (for example, `https://voice.yourdomain.com/voice`) and you can drop ngrok entirely.
</Note>

## How it works

* An inbound call hits `/voice`, and the server returns TwiML that hands the call off to ConversationRelay.
* ConversationRelay handles the call audio and runs speech-to-text (Deepgram by default), then opens a WebSocket to your server and streams user transcripts as they arrive.
* For each user turn, the server calls the Inworld Router API's chat completions endpoint with an `audio` block, which returns an SSE stream of both text deltas and base64-encoded PCM audio chunks. This is the [LLM + TTS](/router/guides/llm-plus-tts) feature.
* Inworld's chunking engine groups the response into text segments at natural sentence boundaries, and each segment's audio may arrive as multiple base64-encoded PCM chunks. The server assembles the audio for each segment, wraps it in a WAV header, hosts it at a short-lived HTTP URL, and sends a `play` message to ConversationRelay so the caller hears the first sentence before the LLM has finished generating the rest.
* On barge-in, ConversationRelay sends an `interrupt` message and the server aborts the in-flight Inworld stream via `AbortController`. Partial responses already spoken are kept in conversation history so context is preserved.

The TwiML returned from `/voice` looks like this:

```xml theme={"system"}
<Response>
  <Connect>
    <ConversationRelay
      url="wss://your-ngrok-domain.ngrok-free.app/conversation"
      transcriptionProvider="Deepgram"
      interruptible="true"
      dtmfDetection="true"
    />
  </Connect>
</Response>
```

## Test your integration

Call your Twilio number. The bot should greet you and hold a conversation.

## Example implementation

<Card icon="github" title="Twilio ConversationRelay integration example" href="https://github.com/inworld-ai/inworld-api-examples/tree/main/integrations/twilio-conversation-relay">
  A complete Node.js reference implementation that bridges Twilio ConversationRelay to the Inworld Router API with LLM + TTS.
</Card>

## Further reading

<CardGroup cols={2}>
  <Card title="LLM + TTS (Voice Responses)" href="/router/guides/llm-plus-tts">
    How to request combined LLM text and Inworld TTS audio in a single streaming call.
  </Card>

  <Card title="ConversationRelay TwiML Reference" href="https://www.twilio.com/docs/voice/twiml/connect/conversationrelay">
    Twilio's reference for configuring ConversationRelay, including voice hints, parameters, and language options.
  </Card>

  <Card title="ConversationRelay WebSocket Protocol" href="https://www.twilio.com/docs/voice/conversationrelay/websocket-messages">
    Full specification of the WebSocket messages exchanged between ConversationRelay and your server.
  </Card>

  <Card title="List TTS Voices" href="/api-reference/voiceAPI/voiceservice/list-voices">
    API reference for fetching available Inworld TTS voice IDs to use in the `audio` block.
  </Card>
</CardGroup>
