Skip to content

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.

IDWhat it isWhere you use it
MS_TEAMS_CLIENT_IDThe Entra app registration Application (client) ID. This identifies the bot.Teams manifest botId. Azure Bot resource. silo env variable.
MS_TEAMS_APP_IDThe Teams app package ID. This names your app in the Teams catalog.Teams manifest id only. Nowhere else in silo.
MS_TEAMS_TENANT_IDYour Entra directory (tenant) GUID.silo env variable only.
MS_TEAMS_CLIENT_SECRETThe 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.

LayerWhere to set it
Entra app registrationAuthentication → Supported account types (signInAudience)
Azure Bot resourcemsaAppType
silo environmentMS_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.
  • msaAppType cannot 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 ​

  1. Open portal.azure.com → Microsoft Entra ID → App registrations → New registration.

  2. Enter a name (for example: Plane Teams Bot).

  3. Choose your account type based on your setup (Single Tenant or Multi-Tenant - see section above).

  4. Select Register.

  5. Copy the Application (client) ID. This is your MS_TEAMS_CLIENT_ID.

  6. Go to Certificates & secrets → New client secret → Enter a description → Add. Copy the secret value immediately. You cannot retrieve it again.

  7. Add the Microsoft Graph permissions. These are required: without them, people can't connect their Microsoft accounts to Plane, and @Plane can't read the conversation it's mentioned in.

    Go to API permissions → Add a permission → Microsoft Graph → Delegated permissions, and add these four:

    PermissionWhy Plane needs itAdmin consent
    User.ReadSigns the user in and reads their profileNo
    offline_accessLets Plane refresh the user's access without signing them in againNo
    ChannelMessage.Read.AllReads channel messages when someone mentions @Plane in a channelYes
    Chat.ReadReads chat messages when someone mentions @Plane in a chatNo

    Then select Grant admin consent for <your tenant>. Check that every permission shows a green Granted status.

  8. Go to Authentication → Add a platform → Web.

  9. 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/callback

    For local development through a tunnel:

    https://<tunnel-host>/silo/api/teams/app/auth/callback
    https://<tunnel-host>/silo/api/teams/user/auth/callback
  10. Under 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.

  1. Open portal.azure.com → Create a resource → search Azure Bot → Create.

  2. Enter a Bot handle (for example: plane-teams-bot).

  3. Choose your subscription and resource group.

  4. Set Pricing tier to F0 (free) or as needed.

  5. Set Type of App to match your Entra account type (Single Tenant or Multi-Tenant).

  6. Select Creation type → Use existing app registration.

  7. Paste your Application (client) ID (MS_TEAMS_CLIENT_ID).

  8. If Single Tenant, also enter your Tenant ID (MS_TEAMS_TENANT_ID).

  9. Select Review + create → Create.

  10. Once created, open the bot resource → Settings → Configuration.

  11. Set the Messaging endpoint:

    https://<silo-host>/silo/api/teams/invoke
  12. Select Apply.

  13. Go to Settings → Channels → find Microsoft Teams → select it → Save. This enables Teams to route messages to your bot.

  14. (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.

sh
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_ID must be a GUID. Do not use common or organizations. An alias breaks token requests and JWT validation.
  • MS_TEAMS_APP_TYPE must match the Entra account type and Azure Bot msaAppType.
  • MS_TEAMS_APP_ID is 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
json
{
  "$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 a configurationUrl, 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.domains must include every host serving Plane links. Link preview won't work for missing hosts. Also add each host to validDomains.

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 ​

  1. Create a folder with three files: manifest.json, color.png, outline.png.

  2. Zip the folder as plane-teams.zip.

  3. Open Microsoft Teams → Apps → Manage your apps → Upload an app → Upload a custom app.

  4. 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>):

bash
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.

ResultMeaningNext step
401✅ Good. silo is running and reachable.Proceed to workspace member setup.
403A credential is missing or invalid. MS_TEAMS_TENANT_ID is not a GUID.Check silo startup logs. Verify all env variables.
404Wrong URL path or SILO_BASE_PATH incorrect.Double-check the path.
502 / timeoutsilo 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_ID is a GUID (not common or organizations).
  • 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 configurableTabs from 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.

Cause: The Plane domain is missing from messageHandlers.value.domains.

Fix:

  • Add your Plane app domain to both messageHandlers.value.domains and validDomains.
  • 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:

bash
az resource show -g <rg> -n <bot-name> \
  --resource-type Microsoft.BotService/botServices \
  --query "properties.endpoint" -o tsv

Update the endpoint:

bash
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, and MS_TEAMS_APP_TYPE all match.
  • [ ] MS_TEAMS_TENANT_ID is a GUID (verified in the Entra portal).
  • [ ] All four env variables set in silo.
  • [ ] silo restarted after env variable changes.
  • [ ] Manifest botId equals MS_TEAMS_CLIENT_ID.
  • [ ] Manifest has no configurableTabs or webApplicationInfo.
  • [ ] Plane app domain added to messageHandlers.value.domains and validDomains.
  • [ ] curl -X POST .../invoke returns 401.

Once all items are checked, proceed to Connect Plane to Microsoft Teams.