api-prod service, two things should happen in their Teams:
- Maya, who is on call, gets a DM from your bot.
#incidentsgets the same alert, so the rest of the team can see it.
Key concepts
The channel or person to notify is stored as an$ms_teams channel on a user or an object. A tenant identifies which customer account a send belongs to.
Tenants identify the customer account
Tenants in SuprSend map to accounts, organizations, or workspaces in your product. Create one tenant per customer (acme) and pass its tenant_id on every workflow trigger for that customer so branding, preferences, and vendor routing apply.
The SuprSend tenant does not hold the customer’s Microsoft Entra tenant ID. SuprSend reads that from each $ms_teams entry, so keep the Entra ID in your own database against the customer account and write it into every entry you store for them.
Each $ms_teams entry names the customer’s Entra directory
Your bot is installed into many customer directories, and one SuprSend workspace serves all of them. That’s the case where tenant_id on $ms_teams matters: it tells SuprSend which directory to deliver into. If you leave it out, SuprSend falls back to the home Tenant ID saved on the vendor — your bot’s directory — and the send never reaches Acme. Include the customer’s Entra tenant ID on every entry you write for them.
Objects hold channels
A Teams channel is a connection on a resource in your product, not on a person. For the incident example, each monitored service is an object in aservices type. $ms_teams on that object is #incidents (or whichever channel Acme picked for that service).
When you trigger a workflow with the service as recipient, SuprSend posts to the channel stored on the object.
Users hold DMs
If Maya should get a DM, store her Teamsuser_id (prefer the Entra object ID) on her SuprSend user. If Maya also uses your product under Northwind, put Acme’s Teams identity on her per-tenant profile so a Northwind incident doesn’t DM her Acme chat.
Merging at send time
Trigger with the object or user as recipient andtenant_id: "acme". SuprSend uses:
- The destination (
conversation_idoruser_id) on the recipient - The Entra
tenant_idon that destination - The workspace vendor (your bot), unless a tenant vendor overrides it
1. Finish the app for other tenants
On Create a Teams app, keep Azure Bot App Type as Single tenant. In Azure App registrations → your app → Authentication, set Supported account types to Multiple Entra ID tenants so Acme’s admin can consent. That is not the vendor App Type.
2. How Acme’s admin installs the app
Put this in Settings → Integrations → Microsoft Teams. An Entra / Teams admin has to click.
Microsoft owns Store review. Sideload while that’s in flight: download the zip from the Developer Portal, then in Acme’s tenant Teams admin center → Teams apps → Manage apps → Upload new app. An administrator in that organization has to approve it. Until your app registration has a verified publisher (MPN ID), their end users cannot consent themselves.
Installing the app in a catalog does not put it in front of anyone.
For channels — in Teams, open Apps from the left rail and find your app under Added by your org or Built for your org. Open it, choose Add to a team, then pick the team that owns
#incidents. Then @mention the bot once in the channel. Adding the app is not enough on its own; the mention puts the bot in the channel roster. It applies team-wide afterwards.


Automate install
Admin consent:$TEAMS_APP_ID is the catalog id from the upload, which can differ from your Entra client id.
3. Confirm the vendor is your bot
Vendors → Microsoft Teams should already have your Application (client) ID, secret, App Type, and your home Tenant ID. That’s the bot Acme is installing. Acme’s Entra id does not go on this form. Empty Access Token +AADSTS700016 → wrong home Tenant ID. Token present but send 401 → App Type doesn’t match the Azure Bot, or a destination is missing the recipient’s tenant_id and fell back to the vendor’s directory.
One MS Teams vendor serves every organization — same App ID, same password, same vendor Tenant ID (your bot’s own directory). What changes per customer is the $ms_teams.tenant_id on the user or object.
Details: Add the vendor in SuprSend.
4. Store destinations
For a first test, copy IDs by hand
You don’t need a picker to prove the bot can post in Acme’s tenant.- In Acme’s Teams, hover
#incidents→ ⋯ → Copy link. - Decode
%3A→:and%40→@. That’sconversation_id. - The link’s
tenantId=query param is Acme’s Entra id — the value you write astenant_idon each$ms_teamsentry. You can also copy it from the Azure portal under Microsoft Entra ID → Overview in their directory.


user_id. Roster 29: ids are tied to this bot registration; recreate the bot and they die. Object IDs come back null on the roster unless the manifest includes RSC such as TeamMember.Read.Group.


Channel on the service object
service_url is optional. Omit it to use https://smba.trafficmanager.net/teams, unless you’re on GCC High or DoD.
DM on the user (per-tenant profile)
user.add_ms_teams without a tenant, it writes the global profile instead.
What to build instead of copy-link
After install, your settings screen should list channels the app can post to, and/or DM this user. Save the channel on the service (object). Save the DM on the user (per-tenant).id is the conversation_id you store (already decoded).
5. Send a test
Create the Teams template. Triggernew-incident with tenant_id: "acme", the api-prod object, and Maya.
Then trigger the same workflow for a second customer.
Acme’s
#incidents and Maya’s DM show the post. Northwind’s send uses Northwind’s tenant and destinations, not Acme’s.If something fails
Works in your tenant, fails in Acme's
Works in your tenant, fails in Acme's
The app isn’t installed there, or
$ms_teams.tenant_id is missing or still your directory. Write Acme’s Entra id as tenant_id on the $ms_teams entry.BotNotInConversationRoster
BotNotInConversationRoster
App is in the tenant but not in that channel. Add it to the team and @mention the bot once.
29: user_id stopped resolving
29: user_id stopped resolving
The bot registration was recreated. Collect Entra object IDs and write those as
user_id.Next
Write the Teams template
Markdown or Adaptive Card content.
Customer brings their own bot
Same objects and users; credentials live on a tenant vendor.