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.

Setup time ~1 hour
Difficulty Moderate (Azure admin needed)
One-time cost Free (Azure Bot is free tier)
Going cost Your existing VPS + model bill

Ingredients

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
Tip: The Teams channel starts automatically once the plugin is available and an 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.

Prefer the portal? You can instead create an Azure Bot resource by hand at portal.azure.com, choosing the Single Tenant type, then set its messaging endpoint to your /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:

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:

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.

Member.Read.Group is what lets OpenClaw resolve who is in a channel via Microsoft Graph — useful for allowlists and for the agent to address people by name. Leave it out and member-aware features silently do nothing.

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