Skip to main content
This guide covers how to send avatar audio and animation to a LiveKit room from your backend. Choose the path that matches how you build your voice agent.

1. Using the LiveKit Agents Framework

If you already use LiveKit Agents to build your voice agent, the simplest option is livekit-plugins-spatialreal. The plugin hooks into your agent pipeline, sends TTS audio to Spatialreal, and publishes the lip-synced avatar stream into your LiveKit room—so you don’t manage the Server SDK or egress config yourself.

How it works

  1. Your agent runs as usual (VAD, STT, LLM, TTS or Realtime), your current AgentSession setup.
  2. The plugin intercepts TTS audio from the agent and sends it to Spatialreal.
  3. Spatialreal generates the avatar stream and publishes it to the same LiveKit room.
  4. Your client join the room and use the Spatialreal RTC client to render the avatar.
Interruption and conversation state are handled inside the plugin.

Python: livekit-plugins-spatialreal

Install
pip install livekit-plugins-spatialreal
Configure
VariableRequiredDescription
SPATIALREAL_API_KEYYesYour Spatialreal API key
SPATIALREAL_APP_IDYesYour Spatialreal app ID
SPATIALREAL_AVATAR_IDYesAvatar to use
SPATIALREAL_CONSOLE_ENDPOINTNoOverride console endpoint
SPATIALREAL_INGRESS_ENDPOINTNoOverride ingress endpoint
LIVEKIT_URLYesYour LiveKit server URL
LIVEKIT_API_KEYYesLiveKit API key
LIVEKIT_API_SECRETYesLiveKit API secret
Use in your agent Create an AvatarSession and start it with your agent session and room. The plugin will attach to the pipeline and publish the avatar to the room.
from livekit.agents import Agent, AgentSession, JobContext, cli, WorkerOptions
from livekit.plugins import spatialreal

async def entrypoint(ctx: JobContext):
    await ctx.connect()

    session = AgentSession(vad=vad, stt=stt, llm=llm, tts=tts)
    avatar = spatialreal.AvatarSession()
    await avatar.start(session, room=ctx.room)

    await session.start(agent=YourAgent(), room=ctx.room)

if __name__ == "__main__":
    cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
Full API and options: livekit-plugins-spatialreal (PyPI: livekit-plugins-spatialreal).

JavaScript plugin (coming soon)

JavaScript/TypeScript plugin for the LiveKit Agents framework will be available soon.

2. Using the Server SDK with LiveKit Egress

If you are not using LiveKit Agents (e.g. custom voice agent server), you can still send avatar output to a LiveKit room by using the Spatialreal AvatarKit Server SDK with LiveKit egress enabled. In this setup:
  • Your server sends audio to the avatar service and passes LiveKit egress config (room URL, credentials, room name, publisher id).
  • The avatar service streams audio + animation directly into the LiveKit room (no need for your server to relay that data).
  • Clients join the room and use @spatialwalk/avatarkit-rtc to render the avatar.
You keep full control over when and how you send audio (and optional interrupt signals; see below), while transport and sync are handled by the service and the RTC client.

Configuration

FieldDescription
urlLiveKit server URL (e.g., wss://your-livekit-server.com)
api_keyLiveKit API key
api_secretLiveKit API secret
room_nameLiveKit room name to publish to
publisher_idPublisher identity in the room

Golang Example

session := avatarsdkgo.NewAvatarSession(
	avatarsdkgo.WithAPIKey("your-api-key"),
	avatarsdkgo.WithAppID("your-app-id"),
	avatarsdkgo.WithAvatarID("your-avatar-id"),
	avatarsdkgo.WithConsoleEndpointURL("https://console.us-west.spatialwalk.cloud/v1/console"),
	avatarsdkgo.WithIngressEndpointURL("wss://api.us-west.spatialwalk.cloud/v2/driveningress"),
	avatarsdkgo.WithExpireAt(time.Now().Add(5 * time.Minute)),
	avatarsdkgo.WithLiveKitEgress(&avatarsdkgo.LiveKitEgressConfig{
		URL:         "wss://your-livekit-server.com",
		APIKey:      "livekit-api-key",
		APISecret:   "livekit-api-secret",
		RoomName:    "your-room-name",
		PublisherID: "avatar-publisher",
	}),
	avatarsdkgo.WithOnError(func(err error) {
		// handle error
	}),
	avatarsdkgo.WithOnClose(func() {
		// handle close
	}),
)

Python Example

from avatarkit import new_avatar_session, LiveKitEgressConfig

session = new_avatar_session(
    api_key="your-api-key",
    app_id="your-app-id",
    avatar_id="your-avatar-id",
    expire_at=datetime.now(timezone.utc) + timedelta(minutes=5),
    console_endpoint_url="https://console.us-west.spatialwalk.cloud/v1/console",
    ingress_endpoint_url="wss://api.us-west.spatialwalk.cloud/v2/driveningress",
    livekit_egress=LiveKitEgressConfig(
        url="wss://your-livekit-server.com",
        api_key="livekit-api-key",
        api_secret="livekit-api-secret",
        room_name="your-room-name",
        publisher_id="avatar-publisher",
    ),
    on_error=lambda err: print(f"Error: {err}"),
    on_close=lambda: print("Session closed"),
)
After the session is created with LiveKit egress, send audio as in normal Server SDK usage; the service will publish to the configured room automatically.

Important Notes

When LiveKit egress is enabled:
  • The TransportFrames / transport_frames callback will not be invoked
  • Audio and animation data are published directly to the specified LiveKit room
  • Your client must use the @spatialwalk/avatarkit-rtc package to render the avatar (standard video players won’t work). See LiveKit Client Guide for client setup.

Interrupt

You can interrupt the avatar (e.g. when the user asks a new question). The interrupt uses the most recent request ID, even after end=true was sent. Golang
// Send audio
requestID, err := session.SendAudio(audioData, true)

// Later, interrupt if needed
interruptedID, err := session.Interrupt()
Python
# Send audio
request_id = await session.send_audio(audio_data, end=True)

# Later, interrupt if needed
interrupted_id = await session.interrupt()

Summary

ApproachBest forWhere to go
LiveKit Agents + PluginPython (or future JS) agents using the LiveKit Agents frameworklivekit-plugins-spatialreal (this page, section 1)
Server SDK + LiveKit egressCustom backends, any language with a Server SDK (Go, Python, etc.)This page, section 2
For client-side setup (joining the room and rendering the avatar), see LiveKit client guide.