Skip to main content
Connect a Twilio phone number to KugelAudio with a bidirectional Media Stream. Twilio opens a WebSocket to a small server of yours. The server turns text (a greeting, an LLM reply) into speech with the KugelAudio Python SDK and sends the audio back into the call. KugelAudio can output G.711 µ-law at 8000 Hz directly (output_format="ulaw_8000"), which is the format Twilio plays. The server forwards each audio chunk as it arrives, with no resampling or transcoding.
Building on LiveKit Agents or Pipecat? Both frameworks connect phone calls for you: LiveKit through SIP trunks and Pipecat through its Twilio WebSocket transport. Use the LiveKit or Pipecat plugin there instead of this page.

How it works

  1. A call reaches your Twilio number. Twilio requests your webhook and gets TwiML with <Connect><Stream>.
  2. Twilio opens a WebSocket to your server and sends connected, then start with the streamSid.
  3. Your server streams KugelAudio TTS with output_format="ulaw_8000" and sends every chunk to Twilio as a media message (base64 µ-law, 8000 Hz, mono).
  4. After each reply the server sends a mark. Twilio echoes it when that audio has finished playing.
  5. When the caller interrupts, the server stops the KugelAudio stream and sends clear, which empties the audio Twilio has buffered.

1. Install

You also need:
  • A KugelAudio API key from the dashboard (Settings, API Keys). See Authentication.
  • A voice ID. Pick one in Voices; the example uses 1071.
  • A Twilio phone number with voice capability.
  • A public HTTPS host for your server. Twilio connects to the stream over wss:// only. For local testing, a tunnel such as ngrok works: ngrok http 8000.

2. The TwiML

Twilio needs this TwiML when a call comes in. The server below returns it from POST /twiml; you can also paste it into a TwiML Bin. Replace YOUR_PUBLIC_HOST with your host name, without https://.
The stream URL cannot carry query string parameters. To pass values such as a customer ID to your server, use nested <Parameter> elements; they arrive in start.customParameters.

3. The server

Save this as server.py. It greets the caller, answers each key press, and stops talking the moment a key is pressed. Key presses stand in for barge-in here so the example runs without speech recognition.
server.py
Run it:
reuse_connection=False gives every reply its own KugelAudio WebSocket. Cancelling a reply closes that connection, so no audio of an interrupted reply reaches the next one.

4. Point your Twilio number at the server

In the Twilio Console, open your phone number’s voice configuration and set the webhook for incoming calls to https://YOUR_PUBLIC_HOST/twiml with HTTP POST. Call the number: you hear the greeting, and pressing a key cuts it off and answers.

Adding speech recognition and an LLM

The example shows where the rest of a voice agent connects:
  • Caller audio. Each inbound media message carries base64 µ-law at 8000 Hz in media.payload. Send it to your speech recognition.
  • Replies. Call call.say(text) with the LLM reply. For lower latency, call it once per complete sentence as the LLM streams; the sentences play in order. See Chunking and latency for where to split, and Voice prompting for prompts that read well aloud.
  • Barge-in. When your voice activity detection hears the caller speak over the agent, call await call.interrupt() instead of reacting to a key press. See Barge-in.
  • End of turn. on_mark runs when a reply has finished playing on the call. Start an end-of-turn timer or open the microphone there.

Twilio messages used

Twilio’s WebSocket messages reference has every field.

Troubleshooting

Twilio could not open the WebSocket. Check that the stream URL starts with wss://, that PUBLIC_HOST is the host only (no https://, no path), and that the tunnel forwards WebSocket upgrades to port 8000.
The audio is not µ-law at 8000 Hz. Keep output_format="ulaw_8000" and do not send a WAV header in media.payload. See Audio formats.
Check KUGELAUDIO_API_KEY. See Authentication and Errors.
Before production, check that webhook requests really come from Twilio; see Twilio’s webhook security guide.

Next steps

Audio formats

Every output format, including µ-law and a-law at 8000 Hz

Barge-in

Stopping speech when the caller interrupts

Voices

Find and pick a voice ID

Regions

Keep traffic in the EU
Last modified on October 16, 2026