fonoster-voice) answers calls for a stream that speaks the fonoster
protocol. It has the same session and the same verbs as the Node.js SDK, in
Python naming. Use it to put your Python agent code, or your model calls, on the phone.
This page assumes you already have a stream and its signing secret. If not,
Build a stream with the Voice SDK shows how to create one and expose your
machine with a tunnel.
Install the SDK
Install the package from PyPI. It needs Python 3.11 or later, and its only dependency iswebsockets:
async def functions that take one argument, the
session. The SDK raises TypeError for anything else.
Run a standalone server
UseVoiceServer when the voice server is its own process. This one greets the caller, listens for
speech or a keypress, and transfers to a person on request:
server.py
server.run() blocks and serves until you stop it. Inside your own event loop, call
await server.listen() instead; it returns once the server is listening.
VoiceServer takes these options:
Mount it in FastAPI
To serve calls from an app you already run, mountvoice_asgi_app on a path. It works with
FastAPI, Starlette and any other ASGI framework:
main.py
wss://bot.example.com/voice, and pass that same
URL as public_url. Plain HTTP requests to the path get 426 WebSocket required.
Stream model output into speech
Pass an async iterator tosay() and the caller hears the text as it arrives, as one continuous
utterance. You don’t wait for the full reply before the caller hears the first words.
This example streams a reply from an OpenAI-compatible model:
Handle barge-in
By default the caller can talk over the agent. When they start talking during asay() or
play(), the SDK stops the speech, and say() returns with interrupted=True. heard_text
holds only the words the caller actually heard:
interruptible=False for text the caller must hear in full, such as a legal notice. The
caller can still talk; what they say is kept for the next gather():
await session.stop_say(). Every pending say() and play()
returns as interrupted.
Speech and keypresses that arrive before you call gather() are kept (up to 32), so callers can
answer before the question ends.
When the caller stops talking is decided by Fonoster’s turn detector, not by silence alone. See
Turn-taking and barge-in.
Listen to events
Register callbacks withsession.on(event, callback). The callback gets the event’s fields as a
dict, and it can be a plain function or a coroutine:
subscribe():
Handle errors
- When Fonoster rejects a command, the call raises
SessionErrorwithcode,messageandfatal. A failed transfer raisesSessionErrorwith codetransfer_failed. - When the call ends while a command is pending, it raises
SessionEndedErrorwithreason, so your handler never hangs. - If your handler raises, the SDK hangs up the call instead of leaving the caller in silence.
fonoster.voice.
How the Python SDK differs from Node.js
The verbs and results are the same. The names follow Python style:
Both SDKs speak the same wire format: see the session protocol.
Next steps
Verify request signatures
What the SDK checks, and how to rotate the secret.
Voice SDK reference
Every method, option and event in Node.js and Python.
Bring your Twilio or Pipecat app
Already have a Pipecat bot? Run it unchanged.
Transfer calls
Cold, warm and SIP REFER transfers.