Browse docs
Docs / channels / msteams
Microsoft Teams
Microsoft Teams (plugin)
“Abandon all hope, ye who enter here.”
Plugin required
text
openclaw plugins install @openclaw/msteamstext
openclaw plugins install ./extensions/msteamsQuick setup (beginner)
- Install the Microsoft Teams plugin.
- Create an Azure Bot (App ID + client secret + tenant ID).
- Configure OpenClaw with those credentials.
- Expose
/api/messages(port 3978 by default) via a public URL or tunnel. - Install the Teams app package and start the gateway.
text
{
channels: {
msteams: {
enabled: true,
appId: "<APP_ID>",
appPassword: "<APP_PASSWORD>",
tenantId: "<TENANT_ID>",
webhook: { port: 3978, path: "/api/messages" },
},
},
}Goals
- Talk to OpenClaw via Teams DMs, group chats, or channels.
- Keep routing deterministic: replies always go back to the channel they arrived on.
- Default to safe channel behavior (mentions required unless configured otherwise).
Config writes
text
{
channels: { msteams: { configWrites: false } },
}Access control (DMs + groups)
- Default:
channels.msteams.dmPolicy = "pairing". Unknown senders are ignored until approved. channels.msteams.allowFromaccepts AAD object IDs, UPNs, or display names. The wizard resolves names to IDs via Microsoft Graph when credentials allow.
- Default:
channels.msteams.groupPolicy = "allowlist"(blocked unless you addgroupAllowFrom). Usechannels.defaults.groupPolicyto override the default when unset. channels.msteams.groupAllowFromcontrols which senders can trigger in group chats/channels (falls back tochannels.msteams.allowFrom).- Set
groupPolicy: "open"to allow any member (still mention‑gated by default). - To allow no channels, set
channels.msteams.groupPolicy: "disabled".
text
{
channels: {
msteams: {
groupPolicy: "allowlist",
groupAllowFrom: ["user@org.com"],
},
},
}- Scope group/channel replies by listing teams and channels under
channels.msteams.teams. - Keys can be team IDs or names; channel keys can be conversation IDs or names.
- When
groupPolicy="allowlist"and a teams allowlist is present, only listed teams/channels are accepted (mention‑gated). - The configure wizard accepts
Team/Channelentries and stores them for you. - On startup, OpenClaw resolves team/channel and user allowlist names to IDs (when Graph permissions allow) and logs the mapping; unresolved entries are kept as typed.
text
{
channels: {
msteams: {
groupPolicy: "allowlist",
teams: {
"My Team": {
channels: {
General: { requireMention: true },
},
},
},
},
},
}How it works
- Install the Microsoft Teams plugin.
- Create an Azure Bot (App ID + secret + tenant ID).
- Build a Teams app package that references the bot and includes the RSC permissions below.
- Upload/install the Teams app into a team (or personal scope for DMs).
- Configure
msteamsin~/.openclaw/openclaw.json(or env vars) and start the gateway. - The gateway listens for Bot Framework webhook traffic on
/api/messagesby default.
Azure Bot Setup (Prerequisites)
Step 1: Create Azure Bot
- Go to Create Azure Bot
- Fill in the Basics tab:
Field Value Bot handle Your bot name, e.g., openclaw-msteams(must be unique)Subscription Select your Azure subscription Resource group Create new or use existing Pricing tier Free for dev/testing Type of App Single Tenant (recommended - see note below) Creation type Create new Microsoft App ID
| Field | Value |
|---|---|
| Bot handle | Your bot name, e.g., openclaw-msteams (must be unique) |
| Subscription | Select your Azure subscription |
| Resource group | Create new or use existing |
| Pricing tier | Free for dev/testing |
| Type of App | Single Tenant (recommended - see note below) |
| Creation type | Create new Microsoft App ID |
Deprecation notice: Creation of new multi-tenant bots was deprecated after 2025-07-31. Use Single Tenant for new bots.
- Click Review + create → Create (wait ~1-2 minutes)
Step 2: Get Credentials
- Go to your Azure Bot resource → Configuration
- Copy Microsoft App ID → this is your
appId - Click Manage Password → go to the App Registration
- Under Certificates & secrets → New client secret → copy the Value → this is your
appPassword - Go to Overview → copy Directory (tenant) ID → this is your
tenantId
Step 3: Configure Messaging Endpoint
- In Azure Bot → Configuration
- Set Messaging endpoint to your webhook URL:
- Production:
https://your-domain.com/api/messages - Local dev: Use a tunnel (see Local Development below)
- Production:
- Production:
https://your-domain.com/api/messages - Local dev: Use a tunnel (see Local Development below)
- Production:
https://your-domain.com/api/messages - Local dev: Use a tunnel (see Local Development below)
Step 4: Enable Teams Channel
- In Azure Bot → Channels
- Click Microsoft Teams → Configure → Save
- Accept the Terms of Service
Local Development (Tunneling)
text
ngrok http 3978
# Copy the https URL, e.g., https://abc123.ngrok.io
# Set messaging endpoint to: https://abc123.ngrok.io/api/messagestext
tailscale funnel 3978
# Use your Tailscale funnel URL as the messaging endpointTeams Developer Portal (Alternative)
- Click + New app
- Fill in basic info (name, description, developer info)
- Go to App features → Bot
- Select Enter a bot ID manually and paste your Azure Bot App ID
- Check scopes: Personal, Team, Group Chat
- Click Distribute → Download app package
- In Teams: Apps → Manage your apps → Upload a custom app → select the ZIP
Testing the Bot
- In Azure Portal → your Azure Bot resource → Test in Web Chat
- Send a message - you should see a response
- This confirms your webhook endpoint works before Teams setup
- Install the Teams app (sideload or org catalog)
- Find the bot in Teams and send a DM
- Check gateway logs for incoming activity
Setup (minimal text-only)
- Install the Microsoft Teams plugin
- From npm:
openclaw plugins install @openclaw/msteams - From a local checkout:
openclaw plugins install ./extensions/msteams
- From npm:
- From npm:
openclaw plugins install @openclaw/msteams - From a local checkout:
openclaw plugins install ./extensions/msteams - Bot registration
- Create an Azure Bot (see above) and note:
- App ID
- Client secret (App password)
- Tenant ID (single-tenant)
- Create an Azure Bot (see above) and note:
- Create an Azure Bot (see above) and note:
- App ID
- Client secret (App password)
- Tenant ID (single-tenant)
- App ID
- Client secret (App password)
- Tenant ID (single-tenant)
- Teams app manifest
- Include a
botentry withbotId = <App ID>. - Scopes:
personal,team,groupChat. supportsFiles: true(required for personal scope file handling).- Add RSC permissions (below).
- Create icons:
outline.png(32x32) andcolor.png(192x192). - Zip all three files together:
manifest.json,outline.png,color.png.
- Include a
- Include a
botentry withbotId = <App ID>. - Scopes:
personal,team,groupChat. supportsFiles: true(required for personal scope file handling).- Add RSC permissions (below).
- Create icons:
outline.png(32x32) andcolor.png(192x192). - Zip all three files together:
manifest.json,outline.png,color.png. - Configure OpenClaw
You can also use environment variables instead of config keys:
MSTEAMS_APP_IDMSTEAMS_APP_PASSWORDMSTEAMS_TENANT_ID
MSTEAMS_APP_IDMSTEAMS_APP_PASSWORDMSTEAMS_TENANT_ID- Bot endpoint
- Set the Azure Bot Messaging Endpoint to:
https://<host>:3978/api/messages(or your chosen path/port).
- Set the Azure Bot Messaging Endpoint to:
- Set the Azure Bot Messaging Endpoint to:
https://<host>:3978/api/messages(or your chosen path/port).
https://<host>:3978/api/messages(or your chosen path/port).- Run the gateway
- The Teams channel starts automatically when the plugin is installed and
msteamsconfig exists with credentials.
- The Teams channel starts automatically when the plugin is installed and
- The Teams channel starts automatically when the plugin is installed and
msteamsconfig exists with credentials.
- From npm:
openclaw plugins install @openclaw/msteams - From a local checkout:
openclaw plugins install ./extensions/msteams
- Create an Azure Bot (see above) and note:
- App ID
- Client secret (App password)
- Tenant ID (single-tenant)
- App ID
- Client secret (App password)
- Tenant ID (single-tenant)
- App ID
- Client secret (App password)
- Tenant ID (single-tenant)
- Include a
botentry withbotId = <App ID>. - Scopes:
personal,team,groupChat. supportsFiles: true(required for personal scope file handling).- Add RSC permissions (below).
- Create icons:
outline.png(32x32) andcolor.png(192x192). - Zip all three files together:
manifest.json,outline.png,color.png.
text
{
"msteams": {
"enabled": true,
"appId": "<APP_ID>",
"appPassword": "<APP_PASSWORD>",
"tenantId": "<TENANT_ID>",
"webhook": { "port": 3978, "path": "/api/messages" }
}
}MSTEAMS_APP_IDMSTEAMS_APP_PASSWORDMSTEAMS_TENANT_ID
- Set the Azure Bot Messaging Endpoint to:
https://<host>:3978/api/messages(or your chosen path/port).
https://<host>:3978/api/messages(or your chosen path/port).
https://<host>:3978/api/messages(or your chosen path/port).
- The Teams channel starts automatically when the plugin is installed and
msteamsconfig exists with credentials.
History context
channels.msteams.historyLimitcontrols how many recent channel/group messages are wrapped into the prompt.- Falls back to
messages.groupChat.historyLimit. Set0to disable (default 50). - DM history can be limited with
channels.msteams.dmHistoryLimit(user turns). Per-user overrides:channels.msteams.dms["<user_id>"].historyLimit.
Current Teams RSC Permissions (Manifest)
ChannelMessage.Read.Group(Application) - receive all channel messages without @mentionChannelMessage.Send.Group(Application)Member.Read.Group(Application)Owner.Read.Group(Application)ChannelSettings.Read.Group(Application)TeamMember.Read.Group(Application)TeamSettings.Read.Group(Application)
ChatMessage.Read.Chat(Application) - receive all group chat messages without @mention
Example Teams Manifest (redacted)
text
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json",
"manifestVersion": "1.23",
"version": "1.0.0",
"id": "00000000-0000-0000-0000-000000000000",
"name": { "short": "OpenClaw" },
"developer": {
"name": "Your Org",
"websiteUrl": "https://example.com",
"privacyUrl": "https://example.com/privacy",
"termsOfUseUrl": "https://example.com/terms"
},
"description": { "short": "OpenClaw in Teams", "full": "OpenClaw in Teams" },
"icons": { "outline": "outline.png", "color": "color.png" },
"accentColor": "#5B6DEF",
"bots": [
{
"botId": "11111111-1111-1111-1111-111111111111",
"scopes": ["personal", "team", "groupChat"],
"isNotificationOnly": false,
"supportsCalling": false,
"supportsVideo": false,
"supportsFiles": true
}
],
"webApplicationInfo": {
"id": "11111111-1111-1111-1111-111111111111"
},
"authorization": {
"permissions": {
"resourceSpecific": [
{ "name": "ChannelMessage.Read.Group", "type": "Application" },
{ "name": "ChannelMessage.Send.Group", "type": "Application" },
{ "name": "Member.Read.Group", "type": "Application" },
{ "name": "Owner.Read.Group", "type": "Application" },
{ "name": "ChannelSettings.Read.Group", "type": "Application" },
{ "name": "TeamMember.Read.Group", "type": "Application" },
{ "name": "TeamSettings.Read.Group", "type": "Application" },
{ "name": "ChatMessage.Read.Chat", "type": "Application" }
]
}
}
}Manifest caveats (must-have fields)
bots[].botIdmust match the Azure Bot App ID.webApplicationInfo.idmust match the Azure Bot App ID.bots[].scopesmust include the surfaces you plan to use (personal,team,groupChat).bots[].supportsFiles: trueis required for file handling in personal scope.authorization.permissions.resourceSpecificmust include channel read/send if you want channel traffic.
Updating an existing app
- Update your
manifest.jsonwith the new settings - Increment the
versionfield (e.g.,1.0.0→1.1.0) - Re-zip the manifest with icons (
manifest.json,outline.png,color.png) - Upload the new zip:
- Option A (Teams Admin Center): Teams Admin Center → Teams apps → Manage apps → find your app → Upload new version
- Option B (Sideload): In Teams → Apps → Manage your apps → Upload a custom app
- Option A (Teams Admin Center): Teams Admin Center → Teams apps → Manage apps → find your app → Upload new version
- Option B (Sideload): In Teams → Apps → Manage your apps → Upload a custom app
- For team channels: Reinstall the app in each team for new permissions to take effect
- Fully quit and relaunch Teams (not just close the window) to clear cached app metadata
- Option A (Teams Admin Center): Teams Admin Center → Teams apps → Manage apps → find your app → Upload new version
- Option B (Sideload): In Teams → Apps → Manage your apps → Upload a custom app
Capabilities: RSC only vs Graph
With Teams RSC only (app installed, no Graph API permissions)
- Read channel message text content.
- Send channel message text content.
- Receive personal (DM) file attachments.
- Channel/group image or file contents (payload only includes HTML stub).
- Downloading attachments stored in SharePoint/OneDrive.
- Reading message history (beyond the live webhook event).
With Teams RSC + Microsoft Graph Application permissions
- Downloading hosted contents (images pasted into messages).
- Downloading file attachments stored in SharePoint/OneDrive.
- Reading channel/chat message history via Graph.
RSC vs Graph API
| Capability | RSC Permissions | Graph API |
|---|---|---|
| Real-time messages | Yes (via webhook) | No (polling only) |
| Historical messages | No | Yes (can query history) |
| Setup complexity | App manifest only | Requires admin consent + token flow |
| Works offline | No (must be running) | Yes (query anytime) |
Graph-enabled media + history (required for channels)
- In Entra ID (Azure AD) App Registration, add Microsoft Graph Application permissions:
ChannelMessage.Read.All(channel attachments + history)Chat.Read.AllorChatMessage.Read.All(group chats)
ChannelMessage.Read.All(channel attachments + history)Chat.Read.AllorChatMessage.Read.All(group chats)- Grant admin consent for the tenant.
- Bump the Teams app manifest version, re-upload, and reinstall the app in Teams.
- Fully quit and relaunch Teams to clear cached app metadata.
ChannelMessage.Read.All(channel attachments + history)Chat.Read.AllorChatMessage.Read.All(group chats)
Known Limitations
Webhook timeouts
- Gateway timeouts
- Teams retrying the message (causing duplicates)
- Dropped replies
Formatting
- Basic formatting works: bold, italic,
code, links - Complex markdown (tables, nested lists) may not render correctly
- Adaptive Cards are supported for polls and arbitrary card sends (see below)
Configuration
channels.msteams.enabled: enable/disable the channel.channels.msteams.appId,channels.msteams.appPassword,channels.msteams.tenantId: bot credentials.channels.msteams.webhook.port(default3978)channels.msteams.webhook.path(default/api/messages)channels.msteams.dmPolicy:pairing | allowlist | open | disabled(default: pairing)channels.msteams.allowFrom: allowlist for DMs (AAD object IDs, UPNs, or display names). The wizard resolves names to IDs during setup when Graph access is available.channels.msteams.textChunkLimit: outbound text chunk size.channels.msteams.chunkMode:length(default) ornewlineto split on blank lines (paragraph boundaries) before length chunking.channels.msteams.mediaAllowHosts: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains).channels.msteams.requireMention: require @mention in channels/groups (default true).channels.msteams.replyStyle:thread | top-level(see Reply Style).channels.msteams.teams.<teamId>.replyStyle: per-team override.channels.msteams.teams.<teamId>.requireMention: per-team override.channels.msteams.teams.<teamId>.tools: default per-team tool policy overrides (allow/deny/alsoAllow) used when a channel override is missing.channels.msteams.teams.<teamId>.toolsBySender: default per-team per-sender tool policy overrides ("*"wildcard supported).channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle: per-channel override.channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention: per-channel override.channels.msteams.teams.<teamId>.channels.<conversationId>.tools: per-channel tool policy overrides (allow/deny/alsoAllow).channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender: per-channel per-sender tool policy overrides ("*"wildcard supported).channels.msteams.sharePointSiteId: SharePoint site ID for file uploads in group chats/channels (see Sending files in group chats).
Routing & Sessions
- Session keys follow the standard agent format (see /concepts/session):
- Direct messages share the main session (
agent:<agentId>:<mainKey>). - Channel/group messages use conversation id:
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
- Direct messages share the main session (
- Direct messages share the main session (
agent:<agentId>:<mainKey>). - Channel/group messages use conversation id:
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
- Direct messages share the main session (
agent:<agentId>:<mainKey>). - Channel/group messages use conversation id:
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
Reply Style: Threads vs Posts
| Style | Description | Recommended replyStyle |
|---|---|---|
| Posts (classic) | Messages appear as cards with threaded replies underneath | thread (default) |
| Threads (Slack-like) | Messages flow linearly, more like Slack | top-level |
threadin a Threads-style channel → replies appear nested awkwardlytop-levelin a Posts-style channel → replies appear as separate top-level posts instead of in-thread
text
{
"msteams": {
"replyStyle": "thread",
"teams": {
"19:abc...@thread.tacv2": {
"channels": {
"19:xyz...@thread.tacv2": {
"replyStyle": "top-level"
}
}
}
}
}
}Attachments & Images
- DMs: Images and file attachments work via Teams bot file APIs.
- Channels/groups: Attachments live in M365 storage (SharePoint/OneDrive). The webhook payload only includes an HTML stub, not the actual file bytes. Graph API permissions are required to download channel attachments.
Sending files in group chats
| Context | How files are sent | Setup needed |
|---|---|---|
| DMs | FileConsentCard → user accepts → bot uploads | Works out of the box |
| Group chats/channels | Upload to SharePoint → share link | Requires sharePointSiteId + Graph permissions |
| Images (any context) | Base64-encoded inline | Works out of the box |
Why group chats need SharePoint
Setup
- Add Graph API permissions in Entra ID (Azure AD) → App Registration:
Sites.ReadWrite.All(Application) - upload files to SharePointChat.Read.All(Application) - optional, enables per-user sharing links
Sites.ReadWrite.All(Application) - upload files to SharePointChat.Read.All(Application) - optional, enables per-user sharing links- Grant admin consent for the tenant.
- Get your SharePoint site ID:
- Configure OpenClaw:
Sites.ReadWrite.All(Application) - upload files to SharePointChat.Read.All(Application) - optional, enables per-user sharing links
text
# Via Graph Explorer or curl with a valid token:
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"
# Example: for a site at "contoso.sharepoint.com/sites/BotFiles"
curl -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"
# Response includes: "id": "contoso.sharepoint.com,guid1,guid2"text
{
channels: {
msteams: {
// ... other config ...
sharePointSiteId: "contoso.sharepoint.com,guid1,guid2",
},
},
}Sharing behavior
| Permission | Sharing behavior |
|---|---|
Sites.ReadWrite.All only | Organization-wide sharing link (anyone in org can access) |
Sites.ReadWrite.All + Chat.Read.All | Per-user sharing link (only chat members can access) |
Fallback behavior
| Scenario | Result |
|---|---|
Group chat + file + sharePointSiteId configured | Upload to SharePoint, send sharing link |
Group chat + file + no sharePointSiteId | Attempt OneDrive upload (may fail), send text only |
| Personal chat + file | FileConsentCard flow (works without SharePoint) |
| Any context + image | Base64-encoded inline (works without SharePoint) |
Files stored location
Polls (Adaptive Cards)
- CLI:
openclaw message poll --channel msteams --target conversation:<id> ... - Votes are recorded by the gateway in
~/.openclaw/msteams-polls.json. - The gateway must stay online to record votes.
- Polls do not auto-post result summaries yet (inspect the store file if needed).
Adaptive Cards (arbitrary)
text
{
"action": "send",
"channel": "msteams",
"target": "user:<id>",
"card": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [{ "type": "TextBlock", "text": "Hello!" }]
}
}text
openclaw message send --channel msteams \
--target "conversation:19:abc...@thread.tacv2" \
--card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello!"}]}'Target formats
| Target type | Format | Example |
|---|---|---|
| User (by ID) | user:<aad-object-id> | user:40a1a0ed-4ff2-4164-a219-55518990c197 |
| User (by name) | user:<display-name> | user:John Smith (requires Graph API) |
| Group/channel | conversation:<conversation-id> | conversation:19:abc123...@thread.tacv2 |
| Group/channel (raw) | <conversation-id> | 19:abc123...@thread.tacv2 (if contains @thread) |
text
# Send to a user by ID
openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello"
# Send to a user by display name (triggers Graph API lookup)
openclaw message send --channel msteams --target "user:John Smith" --message "Hello"
# Send to a group chat or channel
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello"
# Send an Adaptive Card to a conversation
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \
--card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}'text
{
"action": "send",
"channel": "msteams",
"target": "user:John Smith",
"message": "Hello!"
}text
{
"action": "send",
"channel": "msteams",
"target": "conversation:19:abc...@thread.tacv2",
"card": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [{ "type": "TextBlock", "text": "Hello" }]
}
}Proactive messaging
- Proactive messages are only possible after a user has interacted, because we store conversation references at that point.
- See
/gateway/configurationfordmPolicyand allowlist gating.
Team and Channel IDs (Common Gotcha)
text
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
└────────────────────────────┘
Team ID (URL-decode this)text
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
└─────────────────────────┘
Channel ID (URL-decode this)- Team ID = path segment after
/team/(URL-decoded, e.g.,19:Bk4j...@thread.tacv2) - Channel ID = path segment after
/channel/(URL-decoded) - Ignore the
groupIdquery parameter
Private Channels
| Feature | Standard Channels | Private Channels |
|---|---|---|
| Bot installation | Yes | Limited |
| Real-time messages (webhook) | Yes | May not work |
| RSC permissions | Yes | May behave differently |
| @mentions | Yes | If bot is accessible |
| Graph API history | Yes | Yes (with permissions) |
- Use standard channels for bot interactions
- Use DMs - users can always message the bot directly
- Use Graph API for historical access (requires
ChannelMessage.Read.All)
Troubleshooting
Common issues
- Images not showing in channels: Graph permissions or admin consent missing. Reinstall the Teams app and fully quit/reopen Teams.
- No responses in channel: mentions are required by default; set
channels.msteams.requireMention=falseor configure per team/channel. - Version mismatch (Teams still shows old manifest): remove + re-add the app and fully quit Teams to refresh.
- 401 Unauthorized from webhook: Expected when testing manually without Azure JWT - means endpoint is reachable but auth failed. Use Azure Web Chat to test properly.
Manifest upload errors
- “Icon file cannot be empty”: The manifest references icon files that are 0 bytes. Create valid PNG icons (32x32 for
outline.png, 192x192 forcolor.png). - “webApplicationInfo.Id already in use”: The app is still installed in another team/chat. Find and uninstall it first, or wait 5-10 minutes for propagation.
- “Something went wrong” on upload: Upload via https://admin.teams.microsoft.com instead, open browser DevTools (F12) → Network tab, and check the response body for the actual error.
- Sideload failing: Try “Upload an app to your org’s app catalog” instead of “Upload a custom app” - this often bypasses sideload restrictions.
RSC permissions not working
- Verify
webApplicationInfo.idmatches your bot’s App ID exactly - Re-upload the app and reinstall in the team/chat
- Check if your org admin has blocked RSC permissions
- Confirm you’re using the right scope:
ChannelMessage.Read.Groupfor teams,ChatMessage.Read.Chatfor group chats
References
- Create Azure Bot - Azure Bot setup guide
- Teams Developer Portal - create/manage Teams apps
- Teams app manifest schema
- Receive channel messages with RSC
- RSC permissions reference
- Teams bot file handling (channel/group requires Graph)
- Proactive messaging