Microsoft Teams integration Enterprise Grid
After setup
Once you've configured your instance as described on this page, a workspace admin connects the workspace in Plane. See Microsoft Teams integration.
The Microsoft Teams integration requires configuration on Azure before your workspace members can connect Plane to Teams. This page walks through the setup on your Plane instance.
After setup, a workspace admin connects the Plane workspace to your Microsoft tenant, and members connect their own Microsoft accounts. They can then create work items and Intake requests from Teams messages, link messages to existing work items, sync replies in both directions, preview Plane links inline, and ask Plane AI questions.
Before you start
Four IDs exist in this setup.
| ID | What it is | Where you use it |
|---|---|---|
| MS_TEAMS_CLIENT_ID | The Entra app registration Application (client) ID. This identifies the bot. | Teams manifest botId. Azure Bot resource. silo env variable. |
| MS_TEAMS_APP_ID | The Teams app package ID. This names your app in the Teams catalog. | Teams manifest id only. Nowhere else in silo. |
| MS_TEAMS_TENANT_ID | Your Entra directory (tenant) GUID. | silo env variable only. |
| MS_TEAMS_CLIENT_SECRET | The Entra client secret. | silo env variable only. Never in manifests or Azure portal. |
Keep these two rules in mind:
- Teams delivers messages to the bot ID (
MS_TEAMS_CLIENT_ID), not the app ID. If the bot doesn't respond, check the bot identity first. - The messaging endpoint exists only on the Azure Bot resource. It's never in a manifest.
Single tenant vs. multi-tenant
The app type must match in three places. If they disagree, the bot fails silently in one direction.
| Layer | Where to set it |
|---|---|
| Entra app registration | Authentication → Supported account types (signInAudience) |
| Azure Bot resource | msaAppType |
| silo environment | MS_TEAMS_APP_TYPE |
Single tenant - the bot serves only your tenant. Set MS_TEAMS_APP_TYPE="SingleTenant".
Multi-tenant - the bot can serve multiple tenants. Set MS_TEAMS_APP_TYPE="MultiTenant". This is the default if you don't set MS_TEAMS_APP_TYPE. The bot adapter doesn't send a tenant ID in this mode.
Set MS_TEAMS_TENANT_ID to your directory (tenant) GUID in both modes. silo turns on Microsoft Teams only when the client ID, client secret, and a valid tenant GUID are all set.
About multi-tenant setups
As of August 2026:
- Azure doesn't create new multi-tenant bot resources. The create blade only offers Single Tenant.
msaAppTypecannot be changed after creation. A PATCH returns 200 but keeps the old value.
For most setups, choose Single Tenant. If you need one bot for many customers, either keep existing bot registrations from before the change or create one bot per customer.
Create the Entra app registration
Open portal.azure.com → Microsoft Entra ID → App registrations → New registration.
Enter a name (for example:
Plane Teams Bot).Choose your account type based on your setup (Single Tenant or Multi-Tenant - see section above).
Select Register.
Copy the Application (client) ID. This is your
MS_TEAMS_CLIENT_ID.Go to Certificates & secrets → New client secret → Enter a description → Add. Copy the secret value immediately. You cannot retrieve it again.
Add the Microsoft Graph permissions. These are required: without them, people can't connect their Microsoft accounts to Plane, and
@Planecan't read the conversation it's mentioned in.Go to API permissions → Add a permission → Microsoft Graph → Delegated permissions, and add these four:
Permission Why Plane needs it Admin consent User.ReadSigns the user in and reads their profile No offline_accessLets Plane refresh the user's access without signing them in again No ChannelMessage.Read.AllReads channel messages when someone mentions @Planein a channelYes Chat.ReadReads chat messages when someone mentions @Planein a chatNo Then select Grant admin consent for <your tenant>. Check that every permission shows a green Granted status.
Go to Authentication → Add a platform → Web.
Add these two redirect URIs (replace
<silo-host>with your silo domain):https://<silo-host>/silo/api/teams/app/auth/callback https://<silo-host>/silo/api/teams/user/auth/callbackFor local development through a tunnel:
https://<tunnel-host>/silo/api/teams/app/auth/callback https://<tunnel-host>/silo/api/teams/user/auth/callbackUnder Implicit grant and hybrid flows, ensure both toggles are off (disabled).
Create the Azure Bot resource
The Entra app registration is only an identity. The Azure Bot resource holds the messaging endpoint and connects Teams to your bot. Without it, Teams has nowhere to send messages.
Open portal.azure.com → Create a resource → search Azure Bot → Create.
Enter a Bot handle (for example:
plane-teams-bot).Choose your subscription and resource group.
Set Pricing tier to F0 (free) or as needed.
Set Type of App to match your Entra account type (Single Tenant or Multi-Tenant).
Select Creation type → Use existing app registration.
Paste your Application (client) ID (
MS_TEAMS_CLIENT_ID).If Single Tenant, also enter your Tenant ID (
MS_TEAMS_TENANT_ID).Select Review + create → Create.
Once created, open the bot resource → Settings → Configuration.
Set the Messaging endpoint:
https://<silo-host>/silo/api/teams/invokeSelect Apply.
Go to Settings → Channels → find Microsoft Teams → select it → Save. This enables Teams to route messages to your bot.
(Optional) Test the connection: Go to Settings → Test in Web Chat. Send a message and watch your silo logs. You should see activity.
About the CLI
Some az CLI builds don't have az bot commands. Use the portal. Section 8 at the end has CLI commands to read and update the resource after creation.
Configure silo environment variables
Add these four variables to your silo environment. Restart silo after each change; environment variables are read only at startup.
MS_TEAMS_CLIENT_ID="<your Application (client) ID from Step 1>"
MS_TEAMS_CLIENT_SECRET="<your client secret from Step 1>"
MS_TEAMS_TENANT_ID="<your directory (tenant) GUID>"
MS_TEAMS_APP_ID="<your Teams app package ID from Step 4>"
MS_TEAMS_APP_TYPE="SingleTenant"Important rules:
MS_TEAMS_TENANT_IDmust be a GUID. Do not usecommonororganizations. An alias breaks token requests and JWT validation.MS_TEAMS_APP_TYPEmust match the Entra account type and Azure BotmsaAppType.MS_TEAMS_APP_IDis used only when building the Teams app package (Step 4). silo doesn't read it.- Restart silo after any change.
If MS_TEAMS_TENANT_ID is not a GUID, silo starts, writes one error line, and turns off MS Teams only. Other integrations keep working. Teams endpoints return 403 Integration not configured.
Build and upload the Teams app package
The Teams app package is a zip file with three files: manifest.json, color.png (192×192), and outline.png (32×32).
Create manifest.json
Use this template. Replace placeholders:
<MS_TEAMS_CLIENT_ID>- your Application (client) ID<MS_TEAMS_APP_ID>- your Teams app package ID<silo-host>- your silo domain<app-host>- your Plane app domain
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.25/MicrosoftTeams.schema.json",
"manifestVersion": "1.25",
"version": "1.0.0",
"id": "<MS_TEAMS_APP_ID>",
"name": { "short": "Plane", "full": "Plane" },
"developer": {
"name": "Plane",
"websiteUrl": "https://plane.so",
"privacyUrl": "https://plane.so/legals/privacy-policy",
"termsOfUseUrl": "https://plane.so/legals/terms-and-conditions"
},
"description": {
"short": "Create and track Plane work items from Microsoft Teams.",
"full": "Connect Plane to Microsoft Teams. Create work items and intake requests from any message, link an existing work item to a thread so replies sync both ways, preview Plane links inline, and ask Plane AI questions without leaving Teams."
},
"icons": { "outline": "outline.png", "color": "color.png" },
"accentColor": "#3F76FF",
"bots": [
{
"botId": "<MS_TEAMS_CLIENT_ID>",
"scopes": ["personal", "team", "groupChat"],
"commandLists": [
{
"scopes": ["personal"],
"commands": [
{ "title": "create", "description": "Create a new work item in Plane" },
{ "title": "ask", "description": "Ask Plane AI to gather information or act for you" }
]
},
{
"scopes": ["team"],
"commands": [
{ "title": "create", "description": "Create a new work item in Plane" },
{ "title": "ask", "description": "Ask Plane AI to gather information or act for you" }
]
}
],
"isNotificationOnly": false,
"supportsCalling": false,
"supportsVideo": false,
"supportsFiles": false
}
],
"composeExtensions": [
{
"botId": "<MS_TEAMS_CLIENT_ID>",
"canUpdateConfiguration": false,
"commands": [
{
"id": "createWorkItem",
"type": "action",
"title": "Create Work Item",
"description": "Create a new Plane work item from this message",
"initialRun": false,
"fetchTask": true,
"context": ["message"]
},
{
"id": "linkWorkItem",
"type": "action",
"title": "Link Existing Work Item",
"description": "Link an existing Plane work item to this Teams thread",
"initialRun": false,
"fetchTask": true,
"context": ["message"]
}
],
"messageHandlers": [
{
"type": "link",
"value": {
"domains": ["<app-host>"],
"supportsAnonymizedPayloads": true
}
}
]
}
],
"validDomains": ["<silo-host>", "<app-host>"],
"devicePermissions": ["openExternal"],
"authorization": {
"permissions": {
"resourceSpecific": [
{ "name": "ChannelMessage.Send.Group", "type": "Application" },
{ "name": "ChannelMessage.Read.Group", "type": "Application" },
{ "name": "TeamsActivity.Send.Group", "type": "Application" },
{ "name": "ChatMessage.Send.Chat", "type": "Application" },
{ "name": "TeamsActivity.Send.Chat", "type": "Application" },
{ "name": "TeamsActivity.Send.User", "type": "Application" }
]
}
},
"supportsChannelFeatures": "tier1"
}Three critical rules for this manifest:
- Do NOT add
configurableTabs. Teams will send a GET request to aconfigurationUrl, which the bot endpoint doesn't handle. This returns 404, and Teams shows "Unable to reach application." - Do NOT add
webApplicationInfo. This declares bot SSO, which Plane doesn't use. It fails store validation. messageHandlers.value.domainsmust include every host serving Plane links. Link preview won't work for missing hosts. Also add each host tovalidDomains.
Add icons
color.png- 192×192 pixels (the app icon shown in Teams)outline.png- 32×32 pixels (the icon outline)
Save both as PNG files.
Build and upload the package
Create a folder with three files:
manifest.json,color.png,outline.png.Zip the folder as
plane-teams.zip.Open Microsoft Teams → Apps → Manage your apps → Upload an app → Upload a custom app.
Select your zip file.
The app is now available in your Teams tenant.
Test the setup
Run this curl command to test your configuration (replace <silo-host>):
curl -i -X POST 'https://<silo-host>/silo/api/teams/invoke' \
-H 'Content-Type: application/json' \
-d '{"type":"message"}'Expected result: 401 Unauthorized
This means silo is healthy and rejecting the unsigned request correctly. Azure Bot Framework is working.
| Result | Meaning | Next step |
|---|---|---|
| 401 | ✅ Good. silo is running and reachable. | Proceed to workspace member setup. |
| 403 | A credential is missing or invalid. MS_TEAMS_TENANT_ID is not a GUID. | Check silo startup logs. Verify all env variables. |
| 404 | Wrong URL path or SILO_BASE_PATH incorrect. | Double-check the path. |
| 502 / timeout | silo unreachable from the internet. Azure cannot reach it either. | Check firewall and DNS. |
Troubleshooting
Nothing appears in silo logs
Cause: No Azure Bot resource, or the messaging endpoint is blank/wrong, or the Teams channel is disabled.
Fix:
- Verify the Azure Bot resource exists.
- Check the messaging endpoint:
https://<silo-host>/silo/api/teams/invoke - Go to Channels and confirm Microsoft Teams is enabled.
curl returns 403
Cause: MS_TEAMS_TENANT_ID is not a GUID, or it's missing. Check silo startup logs.
Fix:
- Verify
MS_TEAMS_TENANT_IDis a GUID (notcommonororganizations). - Restart silo after fixing.
Teams shows "Unable to reach application. Please try again."
Cause: The manifest has configurableTabs. Teams sends a GET request, gets 404, and shows this error.
Fix:
- Remove
configurableTabsfrom the manifest. - Rebuild and reupload the package.
Bot responds only in one tenant
This is expected. If you set up Single Tenant, the bot only works in that tenant.
Link preview doesn't work
Cause: The Plane domain is missing from messageHandlers.value.domains.
Fix:
- Add your Plane app domain to both
messageHandlers.value.domainsandvalidDomains. - Rebuild and reupload the package.
Azure CLI commands
If your endpoint changes or you need to verify settings, use these commands (replace <rg> and <bot-name>):
Read the current endpoint:
az resource show -g <rg> -n <bot-name> \
--resource-type Microsoft.BotService/botServices \
--query "properties.endpoint" -o tsvUpdate the endpoint:
az resource update -g <rg> -n <bot-name> \
--resource-type Microsoft.BotService/botServices \
--set properties.endpoint="https://<silo-host>/silo/api/teams/invoke"Checklist
Before workspace members connect, verify all of these:
- [ ] Entra app registration exists. You have the client ID and a working secret.
- [ ] All four Microsoft Graph delegated permissions added, with admin consent granted.
- [ ] Both redirect URIs added for each environment (prod, staging, local).
- [ ] Azure Bot resource created with the same client ID.
- [ ] Messaging endpoint set correctly and verified via CLI.
- [ ] Microsoft Teams channel enabled on the bot.
- [ ] Entra account type, bot
msaAppType, andMS_TEAMS_APP_TYPEall match. - [ ]
MS_TEAMS_TENANT_IDis a GUID (verified in the Entra portal). - [ ] All four env variables set in silo.
- [ ] silo restarted after env variable changes.
- [ ] Manifest
botIdequalsMS_TEAMS_CLIENT_ID. - [ ] Manifest has no
configurableTabsorwebApplicationInfo. - [ ] Plane app domain added to
messageHandlers.value.domainsandvalidDomains. - [ ]
curl -X POST .../invokereturns401.
Once all items are checked, proceed to Connect Plane to Microsoft Teams.

