Skip to main content
The Voice SDKs answer calls for a stream that speaks the fonoster protocol. Each call is one session. Both SDKs send the same session protocol messages and have the same methods, in each language’s naming. For a walkthrough, see Build a stream with the Voice SDK or Use the Python Voice SDK.

VoiceServer

VoiceServer accepts Fonoster’s WebSocket connections, checks each one’s signature and runs your handler once per call with a Session.

Options

new VoiceServer(options)
A connection whose signature is missing, more than 300 seconds old, or doesn’t match gets HTTP 401. In Node.js, plain HTTP requests to the listen() server get 426 WebSocket required.

Methods

The handler takes one argument, the session. In Python it must be an async def function with one parameter; anything else raises TypeError. If the handler throws, the SDK hangs up the call.

voice_asgi_app (Python)

voice_asgi_app(handler, *, signing_secret=None, verify_signature_=True, public_url=None) returns an ASGI app to mount in FastAPI, Starlette or any ASGI framework: app.mount("/voice", voice_asgi_app(on_call, signing_secret=..., public_url=...)). HTTP requests get 426; a bad signature closes the WebSocket with code 1008. Note the trailing underscore in verify_signature_.

Session properties

Read-only fields from the call’s session.start message.

Session methods

Speech

SayResult
Speaks text and resolves when it has been heard or cut off. text can be a string or an async iterable of strings: the pieces are spoken as one utterance as they arrive. If the iterable throws, the SDK ends the utterance and rethrows.Options: voice and language (catalog ids that override the stream’s defaults for this utterance), interruptible (default true), preempt (stop what is playing and speak this first). Python takes them as keyword arguments.Resolves to { interrupted, heardText, heardMs } (Python: SayResult(interrupted, heard_text, heard_ms)). heardText holds only the words the caller heard.
void
Python: stop_say(). Sends clear: every pending say and play resolves with interrupted: true.
PlayResult
Plays a WAV file from an http or https URL. Options: loop, interruptible (default true), preempt. Resolves to { interrupted, heardMs } (Python: PlayResult(interrupted, heard_ms)).
GatherResult
Waits for speech or keypresses, whichever comes first, and resolves to { speech }, { digits }, or neither when the timeout passes (Python: GatherResult(speech, digits)).Once a digit arrives, speech is ignored. Only one gather can be pending; a second one rejects. Speech and digits that arrive while no gather is pending are kept (up to 32) and count toward the next one.
void
Node.js only. Starts listening or changes how Fonoster listens: language, hints (words to favor) and patience ("low", "normal" or "high"). Sends listen.update.
void
Turns on opt-in events: "turn.estimate" and "transcript.partial".
Session
Adds or removes an event listener. See Events. In Python the callback gets a dict and can be a plain function or a coroutine. An error thrown by a listener is ignored.

Call control

void
Transfers the call to a phone number or SIP address. Options: mode ("cold", the default, "warm" or "refer") and headers (custom X- SIP headers). Resolves when the session ends with reason transfer. Rejects with SessionError code transfer_failed when the target can’t be reached. See Transfer calls.
void
Python: complete_transfer(). Connects the caller to the party of the pending warm transfer and ends your session; resolves when the session ends. Throws if no warm transfer is pending, and rejects with SessionError code transfer_not_answered before the transfer.answered event (the transfer stays pending).
void
Python: play_dtmf(digits). Sends keypresses on the call: 0–9, A–D, *, #, and w for a pause.
void
Puts the caller on hold with optional music (a URL, looped), or takes them off hold. While on hold you get no caller audio and no turn events; keypresses still arrive.
void
Hands the call to another application in the workspace, by ref. That application gets the payload (up to 16 KB) in handoffPayload. Resolves when your session ends. At most 5 handoffs per call.
void
Hangs up and resolves when the session ends. Resolves at once if it already ended.
void
Does nothing and resolves at once. Fonoster answers the call before your handler runs.

Raw audio

session.audio sends and receives audio in the session’s audioFormat.

Events

Register listeners with session.on(event, callback). The callback gets the event’s fields. end reasons: caller_hangup, app_hangup, transfer, handoff, error, timeout, and closed when the socket closed without a session.end. Speech recognition starts with your first say, play, subscribe, listen or speech gather. Until then there are no turn events.

Barge-in

When the caller starts talking while a say or play is being heard, and every item playing is interruptible, the SDK sends clear and the call resolves with interrupted: true. Items sent with interruptible: false keep playing. Fonoster itself never cuts the agent off; see Turn-taking and barge-in.

Errors

Import both from @fonoster/voice or fonoster.voice.
Error
Fonoster rejected a command. Fields: code, message, fatal. The codes are listed in the session protocol. A fatal error also ends the session.
Error
The session ended while a command was pending, or you sent a command after it ended. Field: reason. Every pending say, play, gather, transfer and handoff rejects with it, so your handler never hangs.

Signature helpers

Use these to verify Fonoster’s connection signature in your own WebSocket server. See Verify request signatures.
Exported from @fonoster/voice: