For the complete documentation index, see llms.txt. This page is also available as Markdown.

Teams Bot

Setting up a Personal-Scope Teams Bot powered by a StackAI workflow

This guide walks through shipping a Microsoft Teams chatbot that:

  • Is pinned to the Teams left rail (looks and feels like Microsoft Copilot's side chat).

  • Receives every user message as a webhook trigger into a StackAI workflow.

  • Posts replies back into the same conversation thread.

End-to-end you will need:

  • An Azure subscription (or Pay-As-You-Go) and Microsoft 365 tenant where you can register apps.

  • Admin permission to upload custom Teams apps to your org (or at least to your own Teams account for sideloading).

  • A StackAI workspace with the Microsoft Teams Bot integration available.


Architecture at a glance

Three Microsoft pieces and one StackAI piece:

  1. Microsoft Entra app registration — gives the bot an identity (App ID + client secret).

  2. Azure Bot resource — registers the bot with Bot Framework and turns on the Teams channel.

  3. Teams app manifest — declares a personal-scope bot so users can pin it.

  4. StackAI workflow — the brain.


Step 1 — Register an Entra (Azure AD) application

This gives the bot its identity.

  1. Go to Azure Portal → Microsoft Entra ID → App registrations → New registration.

  2. Fill in:

    • Name: e.g. StackAI Teams Bot.

    • Supported account types: choose based on who should be able to use the bot:

      • "Single tenant" — only your org.

      • "Multi-tenant" — recommended if you'll distribute to other tenants.

    • Redirect URI: leave this field blank.

  3. Click Register.

  4. On the overview page, copy and save:

    • Application (client) ID → this is the App ID.

    • Directory (tenant) ID → this is the Tenant ID.

  5. Go to Certificates & secrets → New client secret:

    • Description: StackAI Bot.

    • Expiry: 24 months (rotate before expiry).

    • Click Add, then immediately copy the Value (not the Secret ID). This is the Client Secret — Azure will never show it again.

Save these three values somewhere safe:


Step 2 — Create the Azure Bot resource

This is the Bot Framework registration that lets Teams talk to your bot endpoint.

  1. In the Azure Portal, search Azure BotCreate.

  2. Fill in:

    • Bot handle: a unique name like stackai-teams-bot-<orgname>.

    • Subscription and Resource group: pick or create.

    • Pricing tier: F0 (free) is fine for development; use S1 for production.

    • Type of App: Multi Tenant (or Single Tenant — must match what you chose in Step 1).

    • Creation type: Use existing app registration.

    • App ID: paste the App ID from Step 1.

  3. Click Review + create → Create. Wait for deployment.

  4. After deployment, open the new Azure Bot resource. You'll point its messaging endpoint at StackAI in Step 4.

  5. Go to the bot's Channels blade in Settings → click Microsoft Teams → accept terms → Apply.

  6. In the same Channels tab, you should see the Teams channel show Healthy.


Step 3 — Create the connection in StackAI

Before configuring the messaging endpoint, you need the StackAI bot connection so you can grab its webhook URL.

  1. In StackAI, open Integrations → Microsoft Teams Bot → Connect.

  2. Fill in:

    • App ID → from Step 1.

    • Client Secret → from Step 1.

    • Tenant ID → from Step 1 (use common if you registered the app as multi-tenant and want it to work across tenants).

  3. Save the connection. StackAI will validate the credentials by minting a Bot Framework token.

Keep this tab open — you'll come back to it once the workflow trigger exists.


Step 4 — Build the StackAI workflow

The workflow is the brain. At minimum it needs a Teams trigger and a Teams reply action.

4a. Create the workflow

  1. In StackAI, New Project → blank workflow.

  2. Add a Trigger node → choose Microsoft Teams Bot → On New Teams Message.

    • Select the connection you created in Step 3.

    • Save.

Tip: if you want the bot to respond only when @mentioned in a channel, use the On Bot Mentioned trigger instead. For 1:1 personal chat, On New Teams Message is correct.

  1. Add an LLM node (or whatever workflow logic you want).

    • Pipe the trigger's message_text into the prompt.

    • Other useful trigger fields: conversation_id, sender_id, service_url, activity_id.

  2. Add an action node → Microsoft Teams Bot → Reply to Message.

    • Connection: same as the trigger.

    • Conversation ID, Service URL, Reply to Activity ID: map from the trigger output. The integration uses these to thread the reply correctly.

    • Message: pipe in the LLM output.

  3. Save and publish the workflow.

4b. Wire the messaging endpoint to Azure Bot

The Microsoft Teams Bot integration uses a shared, fixed webhook URL for every Teams bot — there is no per-trigger URL generated in the StackAI UI. StackAI routes each inbound activity to the correct workflow using the App ID / Tenant ID on your connection (Step 3).

  1. Back in the Azure Portal → your Azure Bot resource → Settings → Configuration.

  2. Messaging endpoint: paste this URL exactly:

  3. Click Apply.

Azure will now send every Teams Activity (message, mention, etc.) to that URL, and StackAI will dispatch each one to the workflow whose connection matches the bot's App ID.


Step 5 — Create the Teams app manifest

This is what makes the bot show up as a pinnable personal app in Teams. The easiest path is Teams Developer Portal.

  1. Go to https://dev.teams.microsoft.comApps → New app.

  2. Give it a name (StackAI Assistant works) → Add.

  3. Under Basic information, fill in:

    • App ID: click the dice icon to generate a new GUID (this is the Teams app ID — different from the Bot App ID).

    • Short / long descriptions.

    • Developer info, Website, Privacy / TOS URLs (required even for internal apps — point at any reachable HTTPS page).

    • App icons: 192×192 color + 32×32 outline. Required.

  4. Under App features → Bot → Create new bot (or pick existing):

    • When prompted for a Bot ID, paste the App ID from Step 1 (the Entra app — same one used by your Azure Bot resource).

    • Scope: enable Personal (and Team / Group chat if you also want it usable in those contexts).

  5. Save.

Manifest sanity check

Under App features → Bot, verify:

  • Bot ID = your Entra App ID.

  • Personal scope is checked.

You can also add a Personal app tab here if you want a separate web tab next to the chat — not required for the Copilot-style chat experience.


Step 6 — Install the app in Teams

Option A: Sideload for yourself (fastest for testing)

  1. In Teams Developer Portal, click Preview in Teams (top right) on your app.

  2. Teams opens with an install prompt → click Add.

  3. The bot appears in your left rail. Right-click → Pin so it stays.

Option B: Deploy across your organization

This is the production path: a Microsoft 365 admin uploads the bot to your tenant's app catalog and uses App Setup Policies to auto-install and pin it for the right audience — either everyone in the tenant or a specific subgroup (a department, an Entra security group, a pilot team).

You will need a user with one of these roles in your tenant:

  • Teams Administrator (or Global Administrator) for app upload and policy changes.

  • Groups Administrator (or User Administrator) if you need to create the security/M365 group used to scope the rollout.

B1. Submit the app via Dev Portal

  1. In Teams Developer Portal (https://dev.teams.microsoft.com) open your app.

  2. Top right → Distribute → Publish to your org.

  3. Confirm. The app is sent to your tenant's review queue in Teams Admin Center.

Alternative: if you'd rather skip the review queue, click Download app package instead and upload the .zip directly via Teams Admin Center → Manage apps → Actions → Upload new app. End state is the same. Most users find Distribute easier because they don't have to manage a .zip file or remember which Teams Admin Center menu to use.

B2. Approve and unblock the app in Teams Admin Center

Heads up: Teams Admin Center has two independent statuses for every app, and you have to clear both. This trips up almost everyone.

A freshly distributed app typically lands as Submitted + Blocked. You need to publish it (B2.4) and unblock it (B2.5) before users can get it. An app that is Published but Blocked will look approved in the admin UI but will be invisible / un-installable for end users.

Field
Values
Meaning

Publishing status

Submitted / Published

Whether the app exists in your tenant's catalog

Status

Allowed / Blocked

Whether users are permitted to install/use it

  1. Go to Teams Admin Centerhttps://admin.teams.microsoft.com.

  2. Left nav → Teams apps → Manage apps.

  3. Filter by Publishing status: Submitted (or look for the yellow Pending publish badge on your app).

  4. Click your app → Publish button → confirm. Publishing status flips to Published.

  5. Unblock the app:

    • On the app's page, find the Status toggle (top of the page or in the Availability section).

    • Set it to Allowed. If it's already Allowed, leave it.

  6. Permissions tab → if any permissions are listed, click Grant admin consent.

If unblocking the app didn't help

There are two more places that can block a custom app even after the per-app Status is Allowed:

Org-wide app settings (tenant-level kill switch):

  1. Still in Manage apps, top right → Org-wide app settings.

  2. Under Custom apps → ensure Let users interact with custom apps is On.

  3. Under Third-party apps → ensure custom-app interaction isn't disabled there.

  4. Save.

App permission policies (per-user app gating):

  1. Teams apps → Permission policies.

  2. Open the policy assigned to your target user (Global / Org-wide default unless you've made custom ones).

  3. Under Custom apps, ensure it's Allow all apps, or add your specific app to the allowed list.

Most enterprise tenants block custom apps by default for security, so expect to flip at least the per-app Status. The org-wide and permission-policy layers are only an issue in tighter security configurations.

At this point the bot exists in the tenant catalog and is unblocked, but nobody has it installed yet. Distribution to users happens via App Setup Policies (next step).

B3. Decide your audience

Pick one before you touch policies:

Audience
What you need

Everyone in the tenant

Modify the Global (Org-wide default) setup policy

A specific department / team

Create a new custom setup policy, then assign it

A pilot group of named users

Create a new custom setup policy, then assign it

For department/group rollouts, make sure the group exists in Entra ID first:

  • Entra Admin Center → Groups → New group.

  • Group type: Security (cheapest) or Microsoft 365 (if you want a shared mailbox too).

  • Membership type: Assigned for static lists, Dynamic User for rule-based (e.g. department -eq "Customer Success").

  • Add the members. Note the Object ID — you'll reference this group from the Teams policy.

B4. Create or edit the App Setup Policy

A setup policy controls which apps are pre-installed and which apps are pinned to the left rail. You need entries in both sections — installing without pinning means users have to dig through "More apps" to find the bot, which kills adoption.

  1. Teams Admin Center → Teams apps → Setup policies.

  2. Either:

    • Edit Global (Org-wide default) → applies to every user not covered by a custom policy.

    • Add a new custom policy → name it (StackAI Bot Pilot, Customer Success Bots, etc.).

  3. Under Installed appsAdd apps:

    • Search your app name → Add → Add.

    • This makes Teams silently install the app for assigned users on next launch.

  4. Under Pinned appsAdd apps:

    • Search your app name → Add → Add.

    • Drag it to the position you want in the left rail. Top-of-list gets the most usage; below the bottom edge gets ignored.

  5. User pinning: leave on (recommended) so users can re-pin if they accidentally remove it. Turn it off if you want to lock the rail layout.

  6. Save.

Changes propagate to clients within ~24 hours; usually much faster (5–60 min).

B5. Assign the policy to your audience

Skip this step if you edited the Global (Org-wide default) policy — it already applies to everyone.

For a custom policy, assign it one of two ways:

Per-user (small lists):

  1. Teams Admin Center → Users → Manage users.

  2. Filter / select the users.

  3. Edit settings → App setup policy → pick your custom policy → Apply.

Per group (recommended for any rollout > ~10 people):

  1. Teams Admin Center → Teams apps → Setup policies → Group policy assignment tab.

  2. Add group assignment.

  3. Pick the Entra group from B3, choose App setup policy, pick your policy, set rank (lower number = higher priority if a user is in multiple groups).

  4. Apply.

Group assignment is rank-based: each user gets the highest-priority policy they're eligible for. Per-user assignment beats group assignment beats global default.

B6. Verify the rollout

  1. Pick a sample user in the audience. Have them quit and reopen Teams (or wait up to 24h).

  2. The bot should appear pinned in their left rail with no action on their part.

  3. As admin, run Teams Admin Center → Users → [user] → Policies tab to confirm the right setup policy is effective for that user.

  4. Check Manage apps → [your app] → Usage after 24h to see DAU/MAU per the rollout audience.

B7. Shipping updates

When you change the manifest (new icon, new bot name, new scopes):

  1. In Teams Developer Portal, bump the Version field on the app's basic information page (semver — 1.0.01.0.1).

  2. Click Distribute → Publish to your org again.

  3. In Teams Admin Center → Manage apps, find the app and approve the new version.

  4. No policy changes needed — installed users get the new version automatically.

When you change only the workflow logic in StackAI (most common case): nothing to redeploy. The Teams app and Azure Bot are unchanged; users just get the new behavior on the next message.

Common org-wide gotchas

Symptom
Cause
Fix

Some users don't see the bot pinned

They have a higher-priority custom policy without your bot in it

Add the bot to that policy too, or rank yours above it

Bot installed but not pinned

Policy has Installed apps entry but missing Pinned apps entry

Add to Pinned apps in the same policy

App is Published but users still can't install or see it

App's Status is Blocked (default for newly published custom apps)

Manage apps → your app → toggle Status: Allowed

Per-app Status is Allowed but app still blocked for users

Tenant-wide custom-app block, or restrictive App Permission Policy

Manage apps → Org-wide app settings → enable custom apps; check Permission policies for the user's policy

429 / throttling under load

Azure Bot still on F0 tier

Upgrade Azure Bot resource to S1 before broad rollout

Updates not reaching users

Manifest version not bumped

Always increment Version in Dev Portal before re-distributing

Pilot group rolls out to everyone

Custom policy not assigned to a group; only Global policy was edited

Assign custom policy via Group policy assignment and confirm Global policy is unchanged

Recommended rollout pattern

  1. You only (sideload, Option A) — verify the workflow end-to-end.

  2. 5-person pilot — custom setup policy assigned to a small Entra group of friendlies. Run for a week, gather feedback, fix prompts.

  3. Department — assign the same policy to a larger Entra group (e.g. Customer Success dynamic group). Run for 2–4 weeks.

  4. Org-wide — add the bot to the Global setup policy. Retire the pilot policy or keep it for staging changes ahead of the global rollout.

The "feels like Copilot" effect comes entirely from B4's pinning step — without it, users have to find the app manually and adoption craters.


Step 7 — End-to-end smoke test

  1. In Teams, click the pinned app icon → opens a 1:1 chat panel.

  2. Type "hello".

  3. Watch:

    • Azure Bot → Test in Web Chat logs.

    • StackAI → run history for the workflow. You should see a run triggered with the message text.

  4. The reply should land back in Teams within a couple of seconds.

If something is off, see troubleshooting below.


Common gotchas

Symptom
Likely cause
Fix

StackAI run never triggers

Messaging endpoint URL wrong, or Teams channel not enabled on the Azure Bot

Re-paste the StackAI webhook URL exactly; confirm the Teams channel shows Running

401 Unauthorized on inbound

Tenant ID mismatch between Entra app and StackAI connection

For multi-tenant bots use common as the StackAI Tenant ID; for single-tenant use the actual tenant GUID

Reply action fails with Unauthorized

Client secret expired or wrong

Generate a new secret in Entra → update the StackAI connection

Bot never appears in left rail

Org policy doesn't allow custom apps, or app not pinned

Have admin enable custom app sideloading and add a pin policy

Reply lands in a new thread instead of replying inline

Action isn't getting reply_to_activity_id

Check the field mapping from trigger output → Reply to Message action

Works in personal chat but not in channels

Channel scope not enabled on manifest, or you're using the wrong trigger

Add Team scope to manifest, switch trigger to On Bot Mentioned

Slow first response after long idle

Bot Framework token cache cold start

Expected; subsequent replies are fast


Production checklist

Before rolling out to users:


Quick reference

Thing
Where it lives

App ID, Tenant ID, Client Secret

Microsoft Entra ID → App registrations

Messaging endpoint

Azure Portal → Azure Bot → Configuration

Teams channel toggle

Azure Portal → Azure Bot → Channels

StackAI trigger webhook URL

StackAI workflow → Trigger node → "On New Teams Message"

Pinning policy

Teams Admin Center → Teams apps → Setup policies

Last updated

Was this helpful?