Interactive Avatar: LiveKit Plugin reference
Complete reference for the Synthesia plugin for LiveKit Agents, available for Python (livekit-plugins-synthesia) and Node.js (@livekit/agents-plugin-synthesia).
Use this page when you’re already building and need exact signatures, defaults, and failure behavior. For the mental model behind these objects, see Concepts; for runnable code, see the Quickstarts.
In Python, all symbols live in livekit.plugins.synthesia. In Node.js, import the plugin as import * as synthesia from '@livekit/agents-plugin-synthesia’.
Requirements
Python
- Python 3.10 or later. Tested on 3.10, 3.11 and 3.12.
livekit-agents1.8.2 or later, the plugin’s only dependency, installed automatically.
Install the plugin:
If you’re following LiveKit’s docs, livekit-agents[synthesia]~=1.8 installs the same package.
Environment variables
Read when the corresponding argument isn’t passed explicitly.
All five are secrets and belong in your agent’s environment or a secret manager. SYNTHESIA_API_KEY is workspace-bound and must never reach frontend code.
LIVEKIT_URL accepts https:// as well as wss://. The scheme is normalised before it reaches the avatar worker, so the https:// value LiveKit Cloud injects into deployed agents works unchanged.
Minimal usage
The plugin replaces session.output.audio, so the avatar lip-syncs whatever speech your agent produces. You can swap STT, LLM or TTS providers freely and these two lines never change.
synthesia.AvatarSession
Passing an empty or whitespace-only string for either participant field raises SynthesiaError. Omit them to take the defaults.
Properties
synthesia.AvatarConfig
avatar_ids is validated when you construct the config, not when the session starts:
- A bare string instead of a list raises
ValueError. - Fewer than one or more than five ids raises
ValueError.
An id your workspace can’t access raises SynthesiaError with type=ErrorType.UNKNOWN_AVATAR later, at start().
Pass every avatar you might swap to up front. Adding one afterwards requires a new session.
Methods
await avatar.start(agent_session, room, *, livekit_url=None, livekit_api_key=None, livekit_api_secret=None)
Mounts the avatar into room, launches the worker, and wires session.output.audio. Returns once the avatar has joined and published its video track.
Call this before AgentSession.start(). LiveKit credentials fall back to LIVEKIT_URL, LIVEKIT_API_KEY and LIVEKIT_API_SECRET.
The room must already be connected, because the plugin mints the avatar’s token on behalf of your agent’s identity and needs one to exist. Connect the room, then attach the avatar.
Calling start() on an already-started session returns immediately and does nothing. Calling it while another start() is still running raises SynthesiaError.
await avatar.swap_avatar(avatar_id, *, timeout=15.0)
Switches the rendered avatar mid-session to another id from avatar_ids. Pass "default" to return to the first id. Returns the now-active avatar id once the swap has taken effect.
Raises SynthesiaError if the session hasn’t started or is shutting down, type=ErrorType.UNKNOWN_AVATAR if the target wasn’t in avatar_ids, and type=ErrorType.CONNECTION if the swap request to the worker fails. A swap the worker rejects outright raises SynthesiaError with no type set.
await avatar.aclose()
Cooperative shutdown; restores normal audio routing. Called automatically when the room disconnects, or when the avatar drops unexpectedly.
Events
session_ended means you or your user ended the session; error means the avatar went away on its own. Either one triggers shutdown automatically, so you don’t need to call aclose() in the handler.
Exceptions
Every failure raises SynthesiaError, which subclasses LiveKit’s livekit.agents.APIError, so you can catch Synthesia failures alongside every other plugin’s. There is no exception hierarchy below it. To tell failures apart, read the type attribute.
ErrorType
Some failures carry no type at all, including a missing API key and a swap the worker rejects. Handle type is None rather than assuming every SynthesiaError is classified.
Retry guidance
Branch on retryable, not on type; the plugin sets it per failure for exactly this decision. Honor retry_after when it’s set. For a TIMEOUT failure, raise join_timeout before retrying, since cold starts are the usual cause.
Starting a session does not retry internally. A transient server error surfaces immediately as SynthesiaError with type=ErrorType.CONNECTION, so retrying is yours to do.
Never wrap auth, plan, quota, unknown-avatar, or validation failures in a retry loop.
For the HTTP-level error codes behind these error types, see Errors.
Troubleshooting
Prefer automated help?
The Claude Code / Cursor skill can scan your codebase and suggest fixes directly.
Choosing an avatar
Only synthetic and personal avatars work. Stock actor-based avatars (for example Ryan or Ada) can’t be used as interactive avatars, whatever your plan.
For an eligible avatar your plan gives you access to:
- Copy its ID from the Avatars page: open the
•••menu and select Copy ID. - Convert it with
POST /api/interactive-avatars/avatars. Poll untilstatusiscompleted, then pass the returnedidinavatar_ids.
A repeat request for the same source returns the existing interactive avatar rather than starting a new conversion. Converting a stock avatar, or one outside your plan, returns an error rather than an interactive avatar.
Additional resources
- LiveKit’s Synthesia integration guide: LiveKit’s own setup walkthrough for this plugin.
livekit-plugins-synthesiaon PyPI@livekit/agents-plugin-synthesiaon npm