A step-by-step walkthrough of the example agent and bridge: run a LiveKit voice or avatar agent, run the bridge, connect StandIn, and take a Teams call.
The repo ships two example projects so you can see a full working setup - an agent and the
bridge - before wiring your own. A LiveKit call needs both: your agent runs as a worker, and the
bridge dispatches it into a per-call room. Budget about 15 minutes once the
Teams setup is done.
examples/agents
Two ready-to-run LiveKit agents: a minimal voice pipeline (voice_agent.py) and a bitHuman
avatar (avatar_agent.py).
examples/basic-bridge
Embed the bridge in your own Node project: loadConfig() + startServer(), env-file config,
graceful shutdown.
Unlike a hosted agent, a LiveKit “agent” is a worker you run - the example in examples/agents
is a complete, ready-to-run one. You need two things: a LiveKit project for it to connect to, and an
agent name so the bridge can dispatch it.
Sign in to LiveKit Cloud (or run a self-hosted server) and create a
project. From its settings, copy three values - they go in both the agent’s .env (step 1) and
the bridge (step 2):
Value
Env variable
Project URL (wss://<project>.livekit.cloud)
LIVEKIT_URL
API key
LIVEKIT_API_KEY
API secret
LIVEKIT_API_SECRET
The agent worker and the bridge must point at the same LiveKit project. If they use different
projects, dispatch never reaches the agent and the caller hears silence - the single most common
setup mistake.
That name must equal the bridge’s LIVEKIT_AGENT_NAME (step 2). Nothing else about the agent is
Teams-specific - and unlike a hosted agent, there is no audio-format setting to get right, because
the bridge and the LiveKit SDK resample to 16 kHz for you.
Read the caller’s details from ctx.job.metadata and react to the teams.context / teams.goodbye
data topics if you want (all shown in the example agents). See
Agents and dispatch.
Your LiveKit server, e.g. wss://<project>.livekit.cloud.
LIVEKIT_API_KEY / LIVEKIT_API_SECRET
The key pair from your LiveKit project.
OPENAI_API_KEY
Key for the example’s STT/LLM/TTS stack.
Then install and start the worker:
uv sync
uv run voice_agent.py download-files
uv run voice_agent.py dev
(Plain pip works too: pip install -r requirements.txt && python voice_agent.py dev. Use start
instead of dev in production.)The worker registers with your LiveKit server under the agent name teams-voice-agent and
waits for dispatch - it will not join anything until the bridge creates a room.
LIVEKIT_AGENT_NAME must equal the agent_name the worker registers with (the examples use
teams-voice-agent and teams-avatar-agent). A mismatch is the classic silent failure: the room
is created, the caller hears nothing, and the worker never gets a job.
The bridge listens on ws://<host>:8080/voice/msteams/stream.
In your StandIn dashboard, set the identity’s
Agent voice URL to the wss:// URL from step 3.
Make sure the identity’s shared secret equals WORKER_SHARED_SECRET.
Call your Teams bot (or join the sandbox meeting). StandIn joins, connects to the bridge, the
bridge creates a room and dispatches the agent, and the agent answers.
Your agent needs no Teams-specific code, but three integration points are available:
agent_name in WorkerOptions - must match the bridge’s LIVEKIT_AGENT_NAME for explicit
dispatch.
ctx.job.metadata (JSON) - per-call context: source, caller_name, tenant_id,
call_direction, and user_id (AAD id when Teams provides one).
Data topics - teams.context (participant count, DTMF) and teams.goodbye (the governor’s
goodbye line; have your handler speak it and interrupt the current turn).
Embed the bridge with npm install @komaa/livekit-msteams-bridge and start from
examples/basic-bridge
(loadConfig() + startServer()); keep your own agent worker as is - any LiveKit agent works
unchanged. The library API
documents the programmatic surface.
WebSocket rejected with 401 - WORKER_SHARED_SECRET does not match the secret in StandIn.
Room is created but no agent joins - LIVEKIT_AGENT_NAME does not match the worker’s
agent_name, or the worker is not running / registered with a different LiveKit project.
Bot joins the call but stays silent - the Agent voice URL is unreachable from the internet;
re-check the tunnel.