Skip to main content
If you already have a bot that speaks Twilio Media Streams, you can point Fonoster calls at it. Set the stream’s protocol to twilio, and Fonoster sends the same WebSocket messages Twilio sends. A Pipecat bot that uses TwilioFrameSerializer runs as it is. Use this when you already have the bot, or when your framework expects raw audio. To write new call logic, the Voice SDK is simpler: Fonoster does the speech for you.

What works

With the twilio protocol, Fonoster speaks the bidirectional Media Streams messages:
  • Audio is μ-law at 8000 Hz, mono, base64 in media.payload, both ways. Caller audio has track: "inbound".
  • Barge-in works the Twilio way. clear empties the queued audio, and every pending mark is echoed back.
  • Keypresses arrive as dtmf with dtmf.track: "inbound_track" and dtmf.digit.
  • Hanging up: close the WebSocket and Fonoster hangs up the call.
The ids in start map to Fonoster’s:

What isn’t supported

The twilio protocol carries audio, marks and keypresses only. Fonoster ignores any other message and the call continues.
  • No TwiML. Fonoster doesn’t fetch or run TwiML. The stream’s URL is the WebSocket itself.
  • No speech layer. Fonoster doesn’t transcribe the caller or speak text for your bot. Your bot brings its own speech-to-text and text-to-speech, as it does on Twilio. The stream’s voice and speech settings don’t apply.
  • No call control. Your bot can’t transfer the call, put it on hold or send keypresses. If you need those, use the fonoster protocol with the Voice SDK.
  • Audio is μ-law 8 kHz only. The audio format setting applies to fonoster streams.

Point Fonoster at your bot

1

Give your bot a public WebSocket address

Fonoster dials your bot from the internet. Deploy the bot, or run it behind a tunnel, and note the WebSocket URL it serves, such as wss://bot.example.com/ws. Fonoster won’t dial localhost or a private IP.
2

Create a stream with the twilio protocol

  1. Go to Streams and choose New stream.
  2. Name it, for example Pipecat bot.
  3. Enter the bot’s WebSocket address in URL.
  4. Set Protocol to Twilio.
  5. Choose Create stream.
Copy the Signing secret (shown once). It isn’t shown again.
Every connection to your bot is signed with this secret. Your bot runs without checking it, but anyone who finds the URL can open a session. Add the check described in Verify request signatures.
3

Point a number at the stream

Go to Numbers, open the number, and under When this number is called, send it to choose Stream, then your stream. See Numbers and routing for the SDK and CLI.
4

Call the number

Your bot receives connected and start, then the caller’s audio, just as it would from Twilio.

Run a Pipecat bot

A Pipecat bot built for Twilio needs no Fonoster-specific code. parse_telephony_websocket recognizes Fonoster’s start message as Twilio’s, and TwilioFrameSerializer handles the audio, marks and clear. The one setting to check is how the bot hangs up. By default, TwilioFrameSerializer hangs up by calling Twilio’s REST API with your Twilio credentials, which can’t end a Fonoster call. Turn that off and let the bot close the WebSocket instead. Fonoster hangs up when the socket closes:
bot.py
When the pipeline ends, for example after an EndTaskFrame, Pipecat closes the WebSocket and the call ends. Fonoster’s tests run an unmodified Pipecat bot (pipecat-ai 1.12.0) this way: it hears the caller, the caller hears it, clear stops playback on barge-in, keypresses reach it, and it hangs up.
Where the operator turns it on, Fonoster also serves Twilio’s hang-up call: POST {base}/2010-04-01/Accounts/{accountSid}/Calls/{callSid}.json with Status=completed and HTTP Basic auth. {accountSid} is your workspace’s access key ID. It answers 200, or 404 with code 20404 when the call has already ended.Your install’s configuration sets how Basic auth is checked:
  • Workspace API keys: the user is an API key ID (AP…) and the password its secret. The key must belong to the workspace in the URL.
  • One fixed pair (on your own install): the user and the URL’s accountSid are the pair’s accountSid, which must be your workspace’s access key ID, and the password its authToken.
Then point Pipecat’s auto_hang_up at it with base_url. This example uses a fixed pair:
See Twilio compatibility: Hanging up.Closing the socket works on every install, so you don’t need this to hang up.

Next steps

Verify request signatures

Check that each connection comes from Fonoster.

Twilio compatibility

Every Media Streams message and field Fonoster sends and accepts.

Build a stream with the Voice SDK

Need transfers or text-in, text-out speech? Use the fonoster protocol.

Numbers and routing

Send a number’s calls to your stream.