Getting Started
By the end of this page your Cartesia Line agent answers a Microsoft Teams call. You need Python >= 3.10, a Cartesia API key, a deployed Line agent (its agent id), and a StandIn identity (the sandbox is enough).
The agent itself - LLM, tools, conversation logic - is your code on Cartesia’s platform (docs.cartesia.ai/line); this bridge is the Teams transport for it.
1. Install and run the bridge
Section titled “1. Install and run the bridge”pip install cartesia-msteams-bridgeAs a CLI:
CARTESIA_API_KEY=sk_car_... \CARTESIA_AGENT_ID=agent_... \WORKER_SHARED_SECRET=... \ cartesia-msteams-bridgeA .env file in the working directory is loaded automatically (existing environment wins). Or embedded in your own asyncio app:
import asynciofrom cartesia_msteams_bridge import load_config, start_server
async def main(): await start_server(load_config()) # same env variables as the CLI await asyncio.Event().wait()
asyncio.run(main())Every option is an environment variable; the package ships a fully commented .env.example, and the Configuration Reference documents each one. The bridge listens on 0.0.0.0:8080 by default and exposes GET /healthz for liveness checks.
WORKER_SHARED_SECRET comes from StandIn in the next step.
2. Shape the call (optional)
Section titled “2. Shape the call (optional)”Your Line agent’s own deployment config is the default; these override per call:
CARTESIA_INTRODUCTION="Hello! You've reached Komaa. Quick note: I'm an AI assistant."CARTESIA_VOICE_ID=a0e99841-438c-4a64-b679-ae501e7d6091CARTESIA_SYSTEM_PROMPT="You are Komaa's friendly receptionist. Keep replies short."The bridge always passes caller context (name, tenant, direction) to your agent code in the start metadata, and appends it to CARTESIA_SYSTEM_PROMPT when you set one. See Your Line Agent for everything your agent receives.
3. Connect a StandIn identity
Section titled “3. Connect a StandIn identity”StandIn is the hosted service that joins the Teams call and dials into your bridge. Pick a tier at standin.komaa.com (sandbox for an instant trial), pair, and you get a shared secret.
- Put the secret in
WORKER_SHARED_SECRET(both sides must match exactly). - Point the identity’s agent WebSocket URL at your bridge, for example
wss://line-bridge.example.com:8080/voice/msteams/stream. StandIn appends/{callId}per call. - Restart the bridge if you changed the env.
StandIn dials in from the internet, so a laptop or private host needs a public URL. A tunnel gives you one and terminates TLS (so you get wss:// for free). Run one pointing at port 8080, then use the wss://…/voice/msteams/stream form of the printed host:
Tailscale Funnel:
tailscale funnel --bg --https=8080 8080Cloudflare Tunnel:
cloudflared tunnel --url http://localhost:8080ngrok:
ngrok http 8080VS Code dev tunnels:
devtunnel host -p 8080 --allow-anonymousFor a fixed production host use an ingress/load balancer, or serve TLS natively with TLS_CERT_PATH + TLS_KEY_PATH. Never give StandIn a plain ws:// URL outside local testing.
More detail (tiers, what pairing does, cutoff behavior): Connecting to StandIn.
4. Make the first call
Section titled “4. Make the first call”Call your Teams bot (or join the sandbox meeting). In the bridge logs you should see the call arrive, the Line stream open, and the relay start:
INFO [server] worker connected for call 19:meeting_ab... (1/64)INFO [call:19:meeting_ab] session.start (direction=inbound, recording=unknown)INFO [call:19:meeting_ab] Cartesia Line stream open; waiting for ackINFO [call:19:meeting_ab] ack received; relayingINFO [call:19:meeting_ab] first agent audio frame: 640 bytes (20ms if pcm_16000)That last line is the output-format tripwire: Cartesia’s docs imply (but do not state) that agent audio returns in the configured pcm_16000. If your first call sounds wrong, this log line is where to look - see Troubleshooting.