Skip to main content
Pick your runtime. Both paths get you to a live Teams call where your agent answers in voice. Teams chat is a second endpoint on its own port, published and registered separately from voice - see Expose your agent for both lanes in one place.

Prerequisites

  • A working OpenClaw or Hermes Agent install (the host).
  • A StandIn account (standin.komaa.com) - the hosted media bridge that joins the Teams call and connects to your plugin. No worker to run. A paid plan is not required to start: the free identity gives you one BYO-bot identity with 5 minutes of call time per day included.
  • A Teams identity for the bot. You have two ways to get one, and this is the choice that decides how much Azure work you do:
    • Install StandIn from the Microsoft Teams Store. No Azure app registration, no App ID, no client secret, no endpoint. Add the app, type signin, and connect your agent from the StandIn portal. A Microsoft 365 admin approves calling once for the organization; chat works before that.
    • Bring your own Microsoft Teams bot (an Azure AD app) and connect it to StandIn - see Teams setup to create and upload it. Choose this when the bot must carry your own name and live entirely in your tenant.
    Connection modes compares the two in full.
The steps below show the bring-your-own-bot path, because it is the one with setup to describe. If you installed StandIn from the Store, skip the Teams setup entirely: everything from Connect the plugin to StandIn onward is identical.To try StandIn with no Teams identity at all (a shared bot joins a public meeting you generate), use the Sandbox (community tier). It is metered: 5 minutes of bot time per day per room, up to 5 new rooms per day, one bot per meeting, and a 7-day room link, with the call ending at the daily limit.
  • A realtime voice provider key (for realtime mode) - e.g. OpenAI or Azure OpenAI. Streaming mode uses your host’s configured STT/TTS instead.

Install (one line)

The installer detects your OpenClaw or Hermes install, adds the matching plugin, and prompts for the settings (mode, shared secret, provider API key + model, inbound call policy). On a re-run every prompt defaults to your current configuration, so pressing Enter keeps each value. Linux / macOS / WSL
Windows (PowerShell)
It also runs non-interactively - pass flags (--connection, --runtime, --mode, --secret, --provider, --api-key, --model, --port, --messages-port) or set the matching STANDIN_* environment variables.
For secrets (--secret, --api-key), prefer the STANDIN_SECRET / STANDIN_API_KEY environment variables over CLI flags, so the values do not land in your shell history. The installer never transmits your secrets: they are only written to your local plugin config.

StandIn Managed Bot

The default run sets up a bring-your-own-bot connection and pairs it with the portal. If you created a StandIn Managed Bot connection in the dashboard instead (see Connection modes), pass --connection managed and the connection secret the dashboard showed you: Linux / macOS / WSL
Windows (PowerShell)
Use --runtime openclaw (or STANDIN_RUNTIME='openclaw') for OpenClaw. Without the flag the installer asks Connection type interactively. What is different in managed mode:
  • The secret is the connection secret from the dashboard. The installer never generates one for a managed connection: it is the key StandIn already signs with, so a made-up value would turn both lanes off silently. Leave --secret out and the installer prompts for it (masked), which also keeps it out of your shell history.
  • One secret, two lanes. The installer writes the calling lane (9442) and the managed messages lane (9444, --messages-port to change it) into the plugin config, plus the StandIn gateway reply endpoint on Hermes.
  • Two funnel mounts. On Tailscale it mounts /msteams/calling -> 127.0.0.1:9442 and /msteams/messages -> 127.0.0.1:9444, then verifies both. The bring-your-own-bot chat lane (/api/messages -> 3978) is not mounted. On ngrok, cloudflared or devtunnel it prints the two mounts for you to make yourself; see Managed bot chat.
  • No pairing. The connection already exists, so the installer prints the two values to paste into it in the dashboard: Agent calling URL = your public host (StandIn appends /msteams/calling) and Agent chat URL = https://<your-public-host>/msteams/messages. Restart the runtime as the installer says, and calling switches on once your Microsoft 365 admin approves.
Pairing is the zero-config way to connect a subscribed identity: the installer exposes your voice endpoint, sends StandIn the exact wss://host:<voicePort> URL, and delivers the generated shared secret back to the installer automatically. There are no ports to configure and nothing to copy by hand, which avoids the most common first-call issue (a domain saved without its voice port, so StandIn dials the wrong port and the bot silently never joins).
1

Run the installer and choose Pair

Run the one-line installer on the machine your agent runs on. When it asks “Pair this agent with StandIn now?”, choose Pair. It prints an 8-character code.
2

Enter the code at standin.komaa.com/pair

Sign in at standin.komaa.com/pair and enter the code. StandIn shows you the exact voice endpoint and port your agent proposed - confirm it and approve. If you run the official msteams channel plugin, the installer already collected your Azure bot App ID, client secret, and tenant from its config and sent them with the pairing, so the credential fields arrive pre-filled (the secret is consumed server-side and never shown in the browser). Otherwise the installer prompts you for them, or you type them here.
3

Done

The installer picks up the shared secret on its next poll and finishes. Your identity provisions and StandIn dials your agent - you never type a voice URL, a port, or a secret.
Chat is a second lane. It is a second mount (/api/messages in front of 127.0.0.1:3978) and a second field on your connection. The installer provisions it alongside voice on Tailscale, and prints the commands to run yourself on ngrok, cloudflared or devtunnel. Confirm both public paths are there with tailscale funnel status before you test: an older run mounted voice only on Hermes and still reported success, and a missing chat mount is silent - nothing reaches any log. Mount it yourself with Expose your agent. If you are already staring at a silent chat lane, start at Voice works, chat is silent.
At the approve step you choose what the pairing becomes: a paid identity (consumes a plan slot), a free identity (your own Azure bot, 5 minutes/day included, one per account), or the sandbox (no Azure bot at all - a shared StandIn bot joins a meeting StandIn generates; see the Sandbox (community tier)).

Which plan am I on?

Four ways to run StandIn. The first two questions are whether you want to own the Teams bot, and whether you need calls to run longer than 5 minutes. The free identity is the same BYO-bot setup as paid - your own bot, your own tenant, inbound calls to your own Teams identity - with 5 minutes of call time per day, one per account, held alongside any paid identities. Production runs on a paid plan, where calls are not time-capped. The sandbox is the only option that needs no Teams identity of any kind: a shared StandIn bot joins a public meeting StandIn generates, so anyone can join as a guest. See Sandbox (community tier).
If you already run the Teams msteams plugin, use a Free identity instead of the sandbox - the sandbox joins a second (shared) bot, so two bots will be in the meeting, both wired to the same agent.
First chat message ignored? The two runtimes gate DMs differently. OpenClaw drops DMs from unapproved senders silently by default (dmPolicy: pairing) - approve yourself once with openclaw pairing approve msteams <CODE>. Hermes replies to an unknown sender with a pairing code instead of silence - approve it with hermes pairing approve teams <CODE>; only when TEAMS_ALLOWED_USERS is set are unlisted senders dropped silently. See Troubleshooting.

Verify (optional)

Prefer not to pipe a script straight into your shell? Download it, review it, then run it:
Download:
Review it:
Run it:
Always fetch over HTTPS from standin.komaa.com. The installer never transmits your secrets; they are only written to your local plugin config.

Manual install

Prefer to wire it up by hand? Pick your runtime:
1

Install

The package ships prebuilt (v0.1.10+), so there is no build step - install and go.
The two registries carry different names: on npm it is @komaa/openclaw-msteams-bridge (the install above), on ClawHub the listing is clawhub:@komaa/msteams-bridge. Both install the same plugin under the id msteams-bridge, so the config below is the same either way. See the plugin README.
2

Configure

Add a msteams-bridge entry to your OpenClaw config - secret must match the value you set in StandIn:
Full reference → OpenClaw configuration.
3

Run + call

Start the gateway (openclaw gateway run), then in your StandIn dashboard register your Agent calling URL (wss://<your-public-host>/msteams/calling via a path-mapped funnel; local bind ws://<host>:9442/...) and the matching secret. Place a Teams call to your bot.
4

Add the chat lane

Voice and chat are separate endpoints: chat is OpenClaw’s msteams channel on 127.0.0.1:3978, published as /api/messages. Mount it and register the Agent chat URL as described in Expose your agent. Voice-only? Skip this step; nothing else changes.
StandIn connects to your plugin from the internet, so both lanes must be reachable - expose them via a public URL or a tunnel (e.g. Tailscale), not loopback-only. Voice is /msteams/calling in front of 9442 on both runtimes; chat is /api/messages in front of 3978 on both. Expose your agent carries the mount commands and the probes that prove a mount works. A secret mismatch fails the HMAC handshake silently - the most common first-call issue.
Streaming mode (no realtime key needed) is a one-line change - see Modes.