examples/basic-bridge
loadConfig() + startServer() in a few lines, with a custom vision hook (your own model answers
the agent’s look tool) and a custom lookup_order function tool the bridge executes. Env-file
config and graceful shutdown are built in.What you need first
- Node.js
>= 20. - A Deepgram API key with Voice Agent access (console.deepgram.com). No agent to create - the bridge configures each session itself.
- A StandIn identity with its shared secret (from pairing or the dashboard). The sandbox works too if you have no Teams bot yet.
1. Clone and install
2. Configure the environment
The example reads the same env file as the CLI - copy the fully commented template from the repo root:.env and fill in the two required values:
| Variable | What to put there |
|---|---|
DEEPGRAM_API_KEY | Your Deepgram API key (server-side only; never sent to the Teams side). |
WORKER_SHARED_SECRET | The shared secret from StandIn pairing - both sides must match exactly. |
DEEPGRAM_PROMPT (the agent’s personality), DEEPGRAM_GREETING
(a deterministic greeting / AI disclosure), DEEPGRAM_SPEAK_MODEL (the Aura voice), and
DEEPGRAM_THINK_MODEL (the LLM). The example still boots with dummy values, so you can check the
wiring before the credentials are real. Everything else (PORT, MAX_CALL_MINUTES, goodbye
behavior) has sensible defaults - see the configuration reference.
3. Start it
4. Expose port 8080
StandIn connects from the internet, so the port needs a publicwss:// URL. Any tunnel works:
- Tailscale Funnel
- cloudflared
- ngrok
wss://<machine>.<tailnet>.ts.net:8080/voice/msteams/stream5. Connect it to StandIn and call
- In your StandIn dashboard, set the identity’s
Agent voice URL to the
wss://URL from step 4. - Make sure the identity’s shared secret equals
WORKER_SHARED_SECRET. - Place a Teams call to your bot (or join the sandbox meeting). StandIn joins, connects to the bridge, and your Deepgram agent answers.
The vision hook
The example ships a customVisionDescriber in index.mjs. When your agent calls its built-in
look tool, the bridge hands your function the current camera or screen-share frame and returns
your text description to the agent. The example answers with gpt-4o-mini; swap in any
vision-capable model. If you prefer configuration over code, the
VISION_API_URL / VISION_API_KEY / VISION_MODEL variables do the same against any
OpenAI-compatible endpoint.
The custom tool
The example also registers alookup_order function tool:
From example to your own project
Depend on the published package instead of the local checkout:index.mjs as your starting point. The
library API documents the
programmatic surface (config, server options, tools, hooks).
If something does not work
- WebSocket rejected with
401-WORKER_SHARED_SECRETdoes not match the secret in StandIn. - Bot joins the call but stays silent - the Agent voice URL is unreachable from the internet or points at the wrong port; re-check the tunnel.
- Call connects, then ends with
agent-unavailable- the Deepgram key is invalid, or a third-partyDEEPGRAM_THINK_PROVIDERis set withoutDEEPGRAM_THINK_ENDPOINT_URL; the log line carries the underlying error. - More: Troubleshooting.