Teams Deep Links That Take Engineers Straight to the Problem
A deep link can drop an on-call engineer into the exact channel, message, or app tab they need. Here's how to build them so your alerts are one tap from action.
- #microsoft-teams
- #deep-links
- #chatops
- #on-call
- #navigation
- #devops
An alert that says “checkout is degraded, see #checkout-incidents” is making a tired engineer do navigation work. A good alert says the same thing but the link drops them directly into that channel, or into the exact message, or into the app tab showing the dashboard — no searching, no scrolling. Teams deep links are the unglamorous plumbing that makes that happen, and getting the URL formats right is the difference between “one tap to the problem” and “where did that channel go?”
The shapes of a Teams deep link
Teams deep links are URLs under https://teams.microsoft.com/l/.... The useful ones for DevOps:
- Channel:
/l/channel/<channelId>/<channelName>?groupId=<teamId>&tenantId=<tenantId> - Chat with users:
/l/chat/0/0?users=user1@org.com,user2@org.com&message=<prefilled> - A specific message:
/l/message/<channelId>/<messageId>?...— drops you on the exact post - An app / tab:
/l/entity/<appId>/<entityId>?context=<json>— opens your app to a specific view - Start a call:
/l/call/0/0?users=user@org.com&withVideo=true
Everything must be URL-encoded. The channelName segment is decorative (Teams shows it but routes on the ID), but the groupId, tenantId, and IDs are load-bearing — get one wrong and the link silently lands on the wrong place or a “not found.”
Deep-link to a channel from an alert
The highest-value link in a DevOps context is “jump to the incident channel.” If your alerting bot already posts to a channel, you have the IDs it needs. Build the link server-side:
function channelDeepLink({ channelId, channelName, teamId, tenantId }) {
const base = "https://teams.microsoft.com/l/channel";
const name = encodeURIComponent(channelName);
const qs = new URLSearchParams({ groupId: teamId, tenantId }).toString();
return `${base}/${encodeURIComponent(channelId)}/${name}?${qs}`;
}
Then surface it as an Action.OpenUrl button on the adaptive card:
{
"type": "Action.OpenUrl",
"title": "Open #checkout-incidents",
"url": "https://teams.microsoft.com/l/channel/19%3Aabc.../Checkout%20Incidents?groupId=...&tenantId=..."
}
Deep-link to the exact message
When an alert spawns a thread, link people to that thread, not the channel top. The message deep link needs the channel ID and the message ID — and you get the message ID back in the response when your bot posts the card. Capture it:
const res = await context.sendActivity({ attachments: [card] });
// res.id is the message id — store it on the incident record
await incidentStore.update(incidentId, { teamsMessageId: res.id });
Later, a status-page update or a PagerDuty note can link straight to the live discussion. This is the small touch that makes cross-tool incident response feel connected instead of a scavenger hunt.
Deep-link into your own app
If you’ve built tabs or a personal app, you can link directly to a specific entity inside it. The context parameter carries JSON — a subEntityId you define — so the link can open your dashboard already scoped to the right service:
function appDeepLink(appId, entityId, subEntityId) {
const context = encodeURIComponent(JSON.stringify({ subEntityId }));
return `https://teams.microsoft.com/l/entity/${appId}/${entityId}?context=${context}`;
}
// appDeepLink("<appId>", "myOncall", "service:checkout")
Inside the tab, read it back via the Teams JS SDK from app.getContext() (look at context.page.subPageId) and render the right view. This is how “open the deploy dashboard for this service” becomes a single button.
Prefer SDK navigation when you’re already in Teams
Hand-built URLs are right for links that originate outside Teams — emails, PagerDuty notes, status pages. But if you’re navigating from inside your own tab or bot, use the Teams JS SDK calls (pages.navigateToApp, app.openLink) instead of constructing the URL yourself. The SDK survives Teams routing changes and handles the desktop-vs-web client differences that hand-built links sometimes trip over. I cover the in-Teams tab navigation patterns more in the tabs guide; the rule of thumb is external source → URL, internal source → SDK.
The gotchas that waste an afternoon
- Encoding
19:...@thread.tacv2channel IDs. The:and@must be percent-encoded or the link breaks.encodeURIComponenthandles it; string concatenation does not. - Mobile vs desktop. Deep links resolve in the Teams mobile app too, but only if the app is installed. From an email, consider linking to a web fallback if you can’t assume mobile install.
- Tenant boundaries. A deep link with the wrong
tenantIdlands a guest user nowhere. For multi-tenant scenarios, generate the link with the recipient’s tenant, not yours. - Stale message IDs. If a message is deleted, its deep link 404s. Don’t promise a permalink to something ephemeral.
Where this fits
Deep links are the connective tissue that makes Teams feel like the front door to your DevOps tooling instead of one more place to get lost. Put a channel link on every alert card, a message link on every incident record, and an app-entity link wherever a dashboard has a natural “show me this one service” view. For adaptive card snippets with deep-link buttons baked in, see the prompt library, and there’s more Teams tooling in the Microsoft Teams category.
Deep-link URL formats and query parameters can change; verify against the current Teams deep-link documentation before relying on them in production alerts.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.