How to Connect OpenClaw to Microsoft Teams?
A recipe-style guide to giving your self-hosted OpenClaw agent a Microsoft Teams face — so your colleagues talk to it the same way they talk to each other, in a channel or a DM.
Ingredients
- A running OpenClaw gateway — see How to Claw? if you do not have one yet
- A Microsoft 365 tenant where you can upload a custom Teams app (or an admin who can)
- An Azure account — the Azure Bot resource sits on the free tier
- Node.js 18+ for the Microsoft Teams CLI
- A public HTTPS URL that reaches your gateway — a real domain in production, a tunnel for testing
- The AAD (Entra) object IDs of the people allowed to talk to the bot
How the pieces fit together
Unlike Telegram or Slack’s socket mode, Teams does not hold open a
connection to your agent. Instead it works like a webhook: when someone
messages the bot, Microsoft’s Bot Framework service
makes an HTTPS POST to an endpoint you register, and your
agent replies back through the Bot Framework API. The whole flow is:
User types in Teams
→ Bot Framework Service (Microsoft cloud)
→ POST https://your-host/api/messages
→ OpenClaw gateway (port 3978) processes with your model
→ reply via Bot Framework API
→ Teams shows the answer
That single fact drives every requirement below: you need a
publicly reachable, TLS-terminated endpoint at
/api/messages, and you need an Azure Bot registration so
Microsoft knows where to send the traffic and how to authenticate it.
1 Make sure the Teams channel plugin is present
Microsoft Teams is a channel plugin in OpenClaw. It ships bundled with current releases, but on older or trimmed-down installs you may need to add it explicitly:
openclaw plugins install @openclaw/msteams
openclaw plugins list # confirm @openclaw/msteams shows up
msteams block with valid
credentials exists in your config. There is no separate “start”
command — configuring it is enabling it.
2 Expose a public HTTPS endpoint
Teams will only deliver to an HTTPS URL with a valid certificate. In
production this is just your domain in front of the gateway’s
port 3978 — put nginx with a Let’s Encrypt cert
in front of it, the same way the How to Claw?
recipe sets up the agent’s website.
For testing from a laptop, open a tunnel instead. Any of these gives you
a throwaway public URL pointing at local port 3978:
# pick one
cloudflared tunnel --url http://localhost:3978
ngrok http 3978
devtunnel create openclaw-bot --allow-anonymous \
&& devtunnel port create openclaw-bot -p 3978 --protocol https
Whatever you get, the endpoint Microsoft needs is that URL with
/api/messages appended — for example
https://openclaw-bot.example.com/api/messages.
3 Register the bot with Azure
The fastest path is Microsoft’s Teams CLI, which creates the Azure Bot, the app registration, and a client secret in one shot:
npm install -g @microsoft/teams.cli@preview
teams login
teams app create --name "OpenClaw" \
--endpoint "https://<your-public-host>/api/messages"
This prints three values: CLIENT_ID (the bot/app ID),
CLIENT_SECRET, and TENANT_ID.
Copy the secret now — Azure will not show it again.
/api/messages URL and generate a client
secret under the linked app registration. The CLI just automates these
clicks.
4 Write the OpenClaw config
Drop the credentials into ~/.openclaw/openclaw.json under a
channels.msteams block:
{
channels: {
msteams: {
enabled: true,
appId: "<CLIENT_ID>",
appPassword: "<CLIENT_SECRET>",
tenantId: "<TENANT_ID>",
webhook: { port: 3978, path: "/api/messages" },
// ── access control (read step 6 before going live) ──
dmPolicy: "allowlist",
allowFrom: ["<your-aad-object-id>"],
groupPolicy: "allowlist",
requireMention: true
}
}
}
If you would rather keep secrets out of the file, OpenClaw also reads them from the environment:
export MSTEAMS_APP_ID="<CLIENT_ID>"
export MSTEAMS_APP_PASSWORD="<CLIENT_SECRET>"
export MSTEAMS_TENANT_ID="<TENANT_ID>"
What the policy keys mean:
dmPolicy—"pairing"(default),"allowlist","open", or"disabled". Controls who can DM the bot.groupPolicy—"allowlist"(default),"open", or"disabled". Controls which channels/group chats it answers in.requireMention— whentrue(default for channels) the bot only responds when@-mentioned, so it stays quiet in busy channels.allowFrom— an array of AAD object IDs (oraccessGroup:references) permitted to talk to it.
5 Build and upload the Teams app package
Azure routes the messages, but a human still needs to install the bot into a team. That is what the Teams app package is for — a small ZIP with three files:
manifest.json— declares the bot ID, scopes, and permissionscolor.png— 192×192 colour iconoutline.png— 32×32 transparent outline icon
Inside manifest.json, request the
Resource-Specific Consent (RSC) permissions the agent
needs to read and post in channels and group chats:
"authorization": {
"permissions": {
"resourceSpecific": [
{ "name": "ChannelMessage.Read.Group", "type": "Application" },
{ "name": "ChannelMessage.Send.Group", "type": "Application" },
{ "name": "Member.Read.Group", "type": "Application" },
{ "name": "ChatMessage.Read.Chat", "type": "Application" }
]
}
}
Zip the three files (the manifest at the root of the archive, not inside a folder) and upload it: in Teams, go to Apps → Manage your apps → Upload an app → Upload a custom app, or push it tenant-wide through the Teams Admin Center. Add the bot to a team or open a DM with it.
6 Lock down who can talk to it
This is the step people skip and regret. A bot installed in a tenant can be found and messaged by anyone who stumbles onto it. Always set an allowlist by AAD object ID, not by display name or UPN — names change, object IDs do not:
// in channels.msteams
"dmPolicy": "allowlist",
"allowFrom": ["<aad-object-id>", "accessGroup:core-team"],
"groupPolicy": "allowlist"
You can find a user’s object ID in the Entra admin center under Users, or have the agent print the sender’s ID the first time they message it and paste it into the allowlist.
7 Start the gateway and test the seam
Restart the gateway so it picks up the new channel. Because the
msteams block now has credentials, the Teams channel comes up
on its own:
openclaw gateway restart
openclaw gateway logs -f # watch for "msteams channel started"
From Teams, DM the bot or @-mention it in a channel where it is
installed. You should see the message land in the gateway logs and a reply
come back. If nothing arrives, the problem is almost always one of: the
endpoint URL in Azure does not match your live /api/messages
URL, TLS is invalid, or the sender is not on the allowlist.
Moving from tunnel to production
When you graduate from a tunnel to a real domain, update the bot’s messaging endpoint so Azure delivers to the right place:
teams app update --id <teamsAppId> \
--endpoint "https://openclaw.your-domain.com/api/messages"
Make sure port 3978 is reachable behind your reverse proxy,
the certificate is valid (not self-signed), and the allowlist reflects the
real team rather than just your own test account.
What you end up with
Your OpenClaw agent living inside Microsoft Teams: reachable by DM and
by @-mention in channels, restricted to the people you
allowlisted, and answering through the same model and tools it already
uses on every other channel. From here you can layer on file uploads,
meeting-aware actions, and adaptive-card approvals one capability at a
time.
The MCP shortcut for Teams actions
The setup above puts the agent in Teams as a conversational interface. If instead you want the agent to act on Teams — schedule meetings, archive teams, pull chat history — you can wire a Microsoft Teams MCP server (for example via Composio) as a tool, independent of the chat channel. The two are complementary: the channel is how people reach the agent, the MCP server is how the agent reaches Teams.
Further reading
- OpenClaw — Microsoft Teams channel docs
- OpenClaw — all chat channels
- Microsoft Teams developer platform
- Composio — Microsoft Teams MCP for OpenClaw
- How to Claw? — get the agent itself running first
- How to Connect Hermes to Microsoft Teams? — the same job with the other framework