Skip to content

Quickstart

Ten minutes from nothing to a call you can hear. You install the SDK, write a brain that answers one way, create an agent that points at it, and open a page. Everything you skip here has its own page in Build.

Terminal window
pip install voqalize-agent-sdk==0.2.0

No pipecat, no model SDK, no audio library. Pin it. 0.2.0 is where this surface stands; the 0.0.x on PyPI is a different one that will import cleanly and then fail on a name this page uses.

Eight lines. A class, one required callback, three speech frames.

from voqalize.sdk import Brain, Chunk, SpeechEnd, SpeechStart
class Concierge(Brain):
async def greet(self, session):
return "Hi! What can I do for you?"
async def on_user_message(self, session, msg):
yield SpeechStart()
yield Chunk("You said: " + msg.text)
yield SpeechEnd()

msg.text is the finalized transcript of one turn. The three frames are one speech unit — a thing with a start, a middle you can stream, and an end that lets the caller interrupt cleanly. Replace the middle with your model’s stream and this is a real agent. See Your first brain.

from voqalize.sdk import run_session
@app.websocket("/voice")
async def voice(ws: WebSocket, session_id: str): # session_id from ?session_id=
await ws.accept()
await run_session(
_WsChannel(ws),
brain=Concierge, # the class, not an instance
session_id=session_id,
token=ws.headers.get("authorization"),
)

run_session takes a channel — anything with async send(bytes) and async recv() -> bytes — so the route is your framework’s, not ours. _WsChannel is the four-line adapter for whichever framework you run; Inbound server has it written out for FastAPI. The brain= is a class, constructed fresh per session, so no state leaks between calls.

On a laptop or a serverless function that cannot accept inbound, serve(...) dials out instead and the brain above does not change — Where the brain runs.

An agent is our record holding your brain’s URL. Create one, keep the sk_ key it gives you, and set brain_url to the route from step 3 — through the MCP server, which is where every account operation lives, or the console. Locally, put a tunnel in front of the route so the URL is reachable.

One fetch for the connect parameters, then stock pipecat. The runnable example is web; Pipecat also supplies React Native, native iOS and native Android clients for the same connect contract. See Connecting a page and Voqalize and pipecat.

A call reaching your agent is now evidence that your route answered: an agent with no brain cannot take one at all, so there is no hosted greeter standing in for yours. Until 2026-09-01 there was, and a greeting proved only that the call path worked.

A greeting still does not prove your turn handling works. Return a distinctive response from on_user_message, as above, and confirm that response before continuing.