Model
Sessions endpoint
This is the live counterpart of
POST /v1/videos, with the same higgs-avatar model. Video jobs render a finished clip from a still image and an audio file; a realtime session renders continuously, in a room, from audio that arrives as the agent speaks. The avatar does not listen or talk by itself: your voice agent does, and the avatar lip-syncs to its output.How it works
Four participants, three of them yours:- The user joins the room from a browser and publishes a microphone track. They subscribe to the avatar’s video and audio like any other participant’s.
- Your agent (
agent.pybelow) joins the same room as a participant. It forwards the user’s microphone audio to Higgs Realtime, and sends the model’s spoken reply to the avatar over a LiveKit data stream (the LiveKit Agents avatar audio protocol: 24 kHz 16-bit PCM, plus a “playback finished” callback the avatar sends back). The agent never publishes audio itself. - Higgs Realtime holds the conversation over one WebSocket: it listens, detects turns, and answers in speech. Same API as the Higgs Realtime quickstart.
- The avatar is the participant Boson adds to your room, under the identity named in your request. It renders the speech it receives into lip-synced video and publishes video and audio tracks, in sync. It is in the room when the create call returns, and leaves when you release the session, when
max_duration_secondselapses, or when everyone else has left.
/v1/avatar/sessions routes never carry media.
Before you begin
Boson. A Boson API key inBOSON_API_KEY, with available credit (new accounts claim their free trial credit first). One key covers both the avatar session and the Higgs Realtime conversation.
LiveKit. A LiveKit Cloud project (the free tier is enough). The avatar joins LiveKit Cloud project URLs (wss://<your-project>.livekit.cloud); a self-hosted LiveKit server is not supported, and a session pointing at one is refused with 422 invalid_livekit_url. In the Cloud dashboard, create a project, then open its Settings → Keys and add a key (Keys and tokens in LiveKit’s docs). The project URL is shown at the top of the dashboard. Those are the three values every script below reads; rooms need no setup, a room exists as soon as the first participant joins it:
livekit-agents package brings the room client, the token helper and the avatar audio protocol; this page was written against 1.8), plus a WebSocket client:
GET /v1/avatars; Boson presets such as builtin_claire_v1, builtin_emma_v1 and builtin_james_v1 are available to every key:
Step 1: the agent
Save this asagent.py. It does four things, in order: puts the avatar in the room, joins the room itself, runs the conversation with Higgs Realtime while streaming the replies to the avatar, and releases the avatar when you stop it.
agent.py
avatar-demo, waiting for someone to talk to. A few things worth knowing about what the script does:
- Two tokens, both minted by you. The avatar’s token (identity
boson-avatar) lets the avatar join your room and publish camera and microphone tracks; it never needs to subscribe. The agent’s token lets this script hear the user. Boson never sees your LiveKit API secret, only the avatar’s token, and does not inspect it: a token with the wrong identity or grants fails when the avatar tries to join. publisher_identityis the agent. The avatar only accepts audio from that participant, over a data stream.DataStreamAudioOutputfromlivekit-agentsspeaks that protocol:capture_framesends PCM,flushmarks the end of an utterance,clear_buffertells the avatar to drop what it has not played yet (used when the user interrupts), and the avatar reportsplayback_finishedwhen it has played everything. The model produces speech faster than real time, so that event, notresponse.done, is what says the avatar has stopped talking.- One user per session. The script forwards every non-avatar participant’s microphone to the same conversation. For a demo that is one person; a product gives each user their own room, agent and session.
- The conversation is plain Higgs Realtime. The WebSocket,
session.update,input_audio_buffer.appendandresponse.output_audio.deltaare exactly the quickstart; the only difference is where the audio comes from (the user’s LiveKit track) and goes to (the avatar instead of a speaker). - Ctrl-C releases the session. The
finallyblock sends theDELETE. If your process dies without it, Boson still ends the session atmax_duration_secondsor once the room is empty, but a forgotten session keeps its capacity slot until then. The demo starts the avatar before anyone is in the room; in production, create the session when the user joins.
Step 2: talk to it
The user needs a token for the room and a page that joins it. Mint the token with the LiveKit CLI (it reads theLIVEKIT_* variables you exported):
viewer.html. It is the whole client: a Join button and a video element for the avatar:
viewer.html
url is the LIVEKIT_URL you exported above, wss:// prefix included, and token is the string lk token create just printed:
agent.py prints the transcript of what it said. Interrupt it mid-sentence and it stops. That is the whole loop: everything else on this page is reference.
The browser only ever holds a LiveKit room token, which is what LiveKit is designed to hand to clients. Your Boson API key and LiveKit API secret stay in
agent.py on your server.The token expires after the
--valid-for you gave it (an hour above). If the page stops connecting later, rerun the lk token create command and open the page again with the new token.The session object
POST /v1/avatar/sessions returns it with 201; GET and DELETE /v1/avatar/sessions/{session_id} return the current state:
Boson ends every session by itself, even if your
DELETE never arrives: at expires_at, or once every other participant has left the room. Release sessions anyway: a forgotten session keeps its slot until it expires.
Capacity and retries
Avatar capacity is limited, and each session occupies one slot for its whole duration. When every slot is in use,POST /v1/avatar/sessions returns 429 with code: "capacity_exhausted" and a Retry-After header (seconds). Retry after that delay; a slot frees as soon as another session ends. Start the avatar only when a user is actually present, keep max_duration_seconds close to your real call length, and release promptly.
503 responses with code: "service_unavailable" are transient and also carry Retry-After. A 429 with type: rate_limit_error is the ordinary per-key rate limit: back off and retry. A 429 with type: insufficient_quota is a billing refusal and does not recover by retrying; see Common error types.
Creation is not idempotent. If the create call times out, check the room for the avatar participant before retrying, and release any duplicate session.
Sessions run up to max_duration_seconds (3600 s by default, per account); output picks the video size (default 480x640). Every request field, limit and error code is in the Avatar Realtime API reference.
It didn’t work?
POST /v1/avatar/sessions returns 429 capacity_exhausted
POST /v1/avatar/sessions returns 429 capacity_exhausted
Every avatar slot is in use, whatever
avatar_id you ask for. Wait Retry-After seconds and retry. A session you forgot to release still counts against capacity until max_duration_seconds elapses or the room empties; release it with DELETE.POST /v1/avatar/sessions returns 422 invalid_livekit_url
POST /v1/avatar/sessions returns 422 invalid_livekit_url
The avatar only joins LiveKit Cloud projects. Set
LIVEKIT_URL to the project URL exactly as the Cloud dashboard shows it, wss://<your-project>.livekit.cloud, with no port, path or query. A self-hosted LiveKit server is refused.The session is active but no avatar appears in the room
The session is active but no avatar appears in the room
The avatar’s token was refused by LiveKit. Check that it was minted with the same
LIVEKIT_API_KEY/SECRET as the project in LIVEKIT_URL, for identity participant_identity, with room_join, can_publish and can_publish_data, and a TTL longer than the session. Boson does not validate the token; GET the session a few seconds later and you will see it terminated.Video shows, the model answers in the agent's log, but the avatar stays silent and still
Video shows, the model answers in the agent's log, but the avatar stays silent and still
The avatar only accepts audio from
publisher_identity. Make sure the identity the agent connects with is the one you named in the create request, and that you send the audio with DataStreamAudioOutput (not by publishing a microphone track).The avatar never answers
The avatar never answers
The agent hears nothing. In the browser, allow the microphone and check that
setMicrophoneEnabled(true) ran after connect. In agent.py, the track_subscribed handler must see the user’s audio track: the agent’s token needs can_subscribe. With server_vad the model answers on its own when you stop speaking; a realtime error line in the agent’s output means the WebSocket side refused something (an invalid key closes with code 3000, no credit gives insufficient_quota).Next steps
Avatar Realtime API reference
Routes, the session object, transport fields and error codes.
Higgs Realtime
Voices, turn detection, interruptions and tool calling for the conversation behind the avatar.