Microsoft Teams
Overview
This skill helps AI agents build integrations with Microsoft Teams — from simple webhook notifications to full-featured bots and workflow automation. It covers the Microsoft Graph API for team/channel/message management, Bot Framework for conversational bots, incoming webhooks for quick notifications, Adaptive Cards for rich UI, and meeting automation.
Instructions
Choose Integration Type
| Need |
Solution |
Complexity |
| Send notifications to a channel |
Incoming Webhook |
Low |
| Read/write messages, manage teams |
Graph API |
Medium |
| Interactive bot (commands, dialogs) |
Bot Framework |
High |
| No-code automation |
Power Automate |
Low |
Incoming Webhooks (Simplest)
const WEBHOOK_URL = process.env.TEAMS_WEBHOOK_URL;
// Simple text
await fetch(WEBHOOK_URL, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: '**Production Alert**: CPU at 95% on api-server-01' }),
});
// Adaptive Card (rich formatting)
await fetch(WEBHOOK_URL, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'message',
attachments: [{
contentType: 'application/vnd.microsoft.card.adaptive',
content: {
type: 'AdaptiveCard', version: '1.5',
body: [
{ type: 'TextBlock', text: 'Deployment Complete', weight: 'Bolder', size: 'Large' },
{ type: 'FactSet', facts: [
{ title: 'Service', value: 'api-gateway' },
{ title: 'Version', value: 'v2.4.1' },
{ title: 'Environment', value: 'Production' },
]},
],
actions: [{ type: 'Action.OpenUrl', title: 'View Dashboard', url: 'https://grafana.example.com' }],
},
}],
}),
});
Graph API
import { ClientSecretCredential } from '@azure/identity';
import { Client } from '@microsoft/microsoft-graph-client';
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
// Permissions: Team.ReadBasic.All, ChannelMessage.Send, Chat.ReadWrite.All, OnlineMeetings.ReadWrite.All
const credential = new ClientSecretCredential(
process.env.AZURE_TENANT_ID, process.env.AZURE_CLIENT_ID, process.env.AZURE_CLIENT_SECRET
);
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ['https://graph.microsoft.com/.default'],
});
const graphClient = Client.initWithMiddleware({ authProvider });
// Create channel
await graphClient.api(`/teams/${teamId}/channels`).post({
displayName: 'Project Alpha', membershipType: 'standard',
});
// Send message with Adaptive Card
await graphClient.api(`/teams/${teamId}/channels/${channelId}/messages`).post({
body: { contentType: 'html', content: '' },
attachments: [{
id: '1', contentType: 'application/vnd.microsoft.card.adaptive',
content: JSON.stringify({
type: 'AdaptiveCard', version: '1.5',
body: [
{ type: 'TextBlock', text: 'PR #142: Add payment retry logic', weight: 'Bolder' },
{ type: 'FactSet', facts: [{ title: 'Author', value: 'Sarah Chen' }, { title: 'Tests', value: 'All passing' }] },
],
actions: [{ type: 'Action.OpenUrl', title: 'Review PR', url: 'https://github.com/org/repo/pull/142' }],
}),
}],
});
// Create online meeting
const meeting = await graphClient.api(`/users/${organizerId}/onlineMeetings`).post({
subject: 'Sprint Planning',
startDateTime: '2026-03-01T14:00:00Z', endDateTime: '2026-03-01T15:00:00Z',
participants: { attendees: [{ upn: 'sarah@example.com', role: 'attendee' }] },
});
console.log('Join URL:', meeting.joinUrl);
Bot Framework
import { ActivityHandler, TurnContext, CardFactory } from 'botbuilder';
import { BotFrameworkAdapter } from 'botbuilder';
import express from 'express';
class TeamBot extends ActivityHandler {
constructor() {
super();
this.onMessage(async (context: TurnContext, next) => {
const text = context.activity.text?.trim().toLowerCase();
if (text === 'status') {
const card = CardFactory.adaptiveCard({
type: 'AdaptiveCard', version: '1.5',
body: [
{ type: 'TextBlock', text: 'System Status', weight: 'Bolder', size: 'Large' },
{ type: 'FactSet', facts: [
{ title: 'API', value: 'Healthy (42ms)' },
{ title: 'Database', value: 'Healthy (8ms)' },
{ title: 'Queue', value: 'High (1,247 pending)' },
]},
],
});
await context.sendActivity({ attachments: [card] });
} else {
await context.sendActivity('Commands: `status` — system health, `deploy <service>` — deploy');
}
await next();
});
this.onMembersAdded(async (context, next) => {
for (const member of context.activity.membersAdded) {
if (member.id !== context.activity.recipient.id)
await context.sendActivity(`Welcome, ${member.name}! Type \`help\` to see what I can do.`);
}
await next();
});
}
}
const adapter = new BotFrameworkAdapter({
appId: process.env.MICROSOFT_APP_ID, appPassword: process.env.MICROSOFT_APP_PASSWORD,
});
const bot = new TeamBot();
const app = express();
app.post('/api/messages', (req, res) => adapter.process(req, res, (ctx) => bot.run(ctx)));
app.listen(3978);
Examples
Example 1: Set up deployment notifications via webhook
User prompt: "Send a rich notification to our Teams DevOps channel whenever a deployment completes, showing the service name, version, environment, and a link to Grafana."
The agent will:
- Get the Incoming Webhook URL from the Teams channel connector settings
- Construct an Adaptive Card with a
TextBlock header, a FactSet showing service/version/environment/time, and an Action.OpenUrl button linking to the Grafana dashboard
- POST the card payload to the webhook URL from the CI/CD pipeline's post-deployment step
- Add error handling to log failures without blocking the deployment
Example 2: Build a slash-command bot for system health checks
User prompt: "Create a Teams bot that responds to 'status' with a system health card showing API, database, and queue metrics pulled from our monitoring endpoints."
The agent will:
- Set up a Bot Framework project with
BotFrameworkAdapter and an Express server on port 3978
- Implement
onMessage handler that checks for the status command
- Fetch current metrics from the monitoring API endpoints
- Format the results as an Adaptive Card with a
FactSet and status indicators
- Register the bot in Azure Bot Service and install it in the Teams workspace
Guidelines
- Incoming webhooks for simple notifications — don't over-engineer with Graph API when a webhook does the job
- Use Adaptive Cards for any structured data — much better UX than plain text
- Graph API with application permissions for daemon services, delegated permissions for user-context apps
- Bot Framework for interactive scenarios — commands, dialogs, approval workflows
- Rate limits: Graph API allows 10,000 requests per 10 minutes per app per tenant
- Always handle
onMembersAdded in bots — send welcome message with capabilities
- Store tokens securely — Azure Key Vault or environment variables, never in code
- Test Adaptive Cards at adaptivecards.io/designer before deploying
- Use Teams Toolkit (VS Code extension) for rapid development with templates
- For high-volume messaging, use batch requests (
$batch endpoint) to reduce API calls
1---2name: microsoft-teams3description: Build bots, automate workflows, and manage Microsoft Teams programmatically via Microsoft Graph API. Use when someone asks to "build a Teams bot", "automate Teams messages", "create Teams channels", "integrate with Teams", "Teams webhook", "send Teams notifications", "manage Teams meetings", or "Teams app development". Covers Graph API for messaging, channels, meetings, bots with Bot Framework, incoming webhooks, and Power Automate integration.4license: Apache-2.05---67# Microsoft Teams89## Overview1011This skill helps AI agents build integrations with Microsoft Teams — from simple webhook notifications to full-featured bots and workflow automation. It covers the Microsoft Graph API for team/channel/message management, Bot Framework for conversational bots, incoming webhooks for quick notifications, Adaptive Cards for rich UI, and meeting automation.1213## Instructions1415### Choose Integration Type1617| Need | Solution | Complexity |18|------|----------|------------|19| Send notifications to a channel | Incoming Webhook | Low |20| Read/write messages, manage teams | Graph API | Medium |21| Interactive bot (commands, dialogs) | Bot Framework | High |22| No-code automation | Power Automate | Low |2324### Incoming Webhooks (Simplest)2526```typescript27const WEBHOOK_URL = process.env.TEAMS_WEBHOOK_URL;2829// Simple text30await fetch(WEBHOOK_URL, {31 method: 'POST', headers: { 'Content-Type': 'application/json' },32 body: JSON.stringify({ text: '**Production Alert**: CPU at 95% on api-server-01' }),33});3435// Adaptive Card (rich formatting)36await fetch(WEBHOOK_URL, {37 method: 'POST', headers: { 'Content-Type': 'application/json' },38 body: JSON.stringify({39 type: 'message',40 attachments: [{41 contentType: 'application/vnd.microsoft.card.adaptive',42 content: {43 type: 'AdaptiveCard', version: '1.5',44 body: [45 { type: 'TextBlock', text: 'Deployment Complete', weight: 'Bolder', size: 'Large' },46 { type: 'FactSet', facts: [47 { title: 'Service', value: 'api-gateway' },48 { title: 'Version', value: 'v2.4.1' },49 { title: 'Environment', value: 'Production' },50 ]},51 ],52 actions: [{ type: 'Action.OpenUrl', title: 'View Dashboard', url: 'https://grafana.example.com' }],53 },54 }],55 }),56});57```5859### Graph API6061```typescript62import { ClientSecretCredential } from '@azure/identity';63import { Client } from '@microsoft/microsoft-graph-client';64import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';6566// Permissions: Team.ReadBasic.All, ChannelMessage.Send, Chat.ReadWrite.All, OnlineMeetings.ReadWrite.All67const credential = new ClientSecretCredential(68 process.env.AZURE_TENANT_ID, process.env.AZURE_CLIENT_ID, process.env.AZURE_CLIENT_SECRET69);70const authProvider = new TokenCredentialAuthenticationProvider(credential, {71 scopes: ['https://graph.microsoft.com/.default'],72});73const graphClient = Client.initWithMiddleware({ authProvider });7475// Create channel76await graphClient.api(`/teams/${teamId}/channels`).post({77 displayName: 'Project Alpha', membershipType: 'standard',78});7980// Send message with Adaptive Card81await graphClient.api(`/teams/${teamId}/channels/${channelId}/messages`).post({82 body: { contentType: 'html', content: '' },83 attachments: [{84 id: '1', contentType: 'application/vnd.microsoft.card.adaptive',85 content: JSON.stringify({86 type: 'AdaptiveCard', version: '1.5',87 body: [88 { type: 'TextBlock', text: 'PR #142: Add payment retry logic', weight: 'Bolder' },89 { type: 'FactSet', facts: [{ title: 'Author', value: 'Sarah Chen' }, { title: 'Tests', value: 'All passing' }] },90 ],91 actions: [{ type: 'Action.OpenUrl', title: 'Review PR', url: 'https://github.com/org/repo/pull/142' }],92 }),93 }],94});9596// Create online meeting97const meeting = await graphClient.api(`/users/${organizerId}/onlineMeetings`).post({98 subject: 'Sprint Planning',99 startDateTime: '2026-03-01T14:00:00Z', endDateTime: '2026-03-01T15:00:00Z',100 participants: { attendees: [{ upn: 'sarah@example.com', role: 'attendee' }] },101});102console.log('Join URL:', meeting.joinUrl);103```104105### Bot Framework106107```typescript108import { ActivityHandler, TurnContext, CardFactory } from 'botbuilder';109import { BotFrameworkAdapter } from 'botbuilder';110import express from 'express';111112class TeamBot extends ActivityHandler {113 constructor() {114 super();115 this.onMessage(async (context: TurnContext, next) => {116 const text = context.activity.text?.trim().toLowerCase();117 if (text === 'status') {118 const card = CardFactory.adaptiveCard({119 type: 'AdaptiveCard', version: '1.5',120 body: [121 { type: 'TextBlock', text: 'System Status', weight: 'Bolder', size: 'Large' },122 { type: 'FactSet', facts: [123 { title: 'API', value: 'Healthy (42ms)' },124 { title: 'Database', value: 'Healthy (8ms)' },125 { title: 'Queue', value: 'High (1,247 pending)' },126 ]},127 ],128 });129 await context.sendActivity({ attachments: [card] });130 } else {131 await context.sendActivity('Commands: `status` — system health, `deploy <service>` — deploy');132 }133 await next();134 });135 this.onMembersAdded(async (context, next) => {136 for (const member of context.activity.membersAdded) {137 if (member.id !== context.activity.recipient.id)138 await context.sendActivity(`Welcome, ${member.name}! Type \`help\` to see what I can do.`);139 }140 await next();141 });142 }143}144145const adapter = new BotFrameworkAdapter({146 appId: process.env.MICROSOFT_APP_ID, appPassword: process.env.MICROSOFT_APP_PASSWORD,147});148const bot = new TeamBot();149const app = express();150app.post('/api/messages', (req, res) => adapter.process(req, res, (ctx) => bot.run(ctx)));151app.listen(3978);152```153154## Examples155156### Example 1: Set up deployment notifications via webhook157**User prompt:** "Send a rich notification to our Teams DevOps channel whenever a deployment completes, showing the service name, version, environment, and a link to Grafana."158159The agent will:1601. Get the Incoming Webhook URL from the Teams channel connector settings1612. Construct an Adaptive Card with a `TextBlock` header, a `FactSet` showing service/version/environment/time, and an `Action.OpenUrl` button linking to the Grafana dashboard1623. POST the card payload to the webhook URL from the CI/CD pipeline's post-deployment step1634. Add error handling to log failures without blocking the deployment164165### Example 2: Build a slash-command bot for system health checks166**User prompt:** "Create a Teams bot that responds to 'status' with a system health card showing API, database, and queue metrics pulled from our monitoring endpoints."167168The agent will:1691. Set up a Bot Framework project with `BotFrameworkAdapter` and an Express server on port 39781702. Implement `onMessage` handler that checks for the `status` command1713. Fetch current metrics from the monitoring API endpoints1724. Format the results as an Adaptive Card with a `FactSet` and status indicators1735. Register the bot in Azure Bot Service and install it in the Teams workspace174175## Guidelines176177- Incoming webhooks for simple notifications — don't over-engineer with Graph API when a webhook does the job178- Use Adaptive Cards for any structured data — much better UX than plain text179- Graph API with application permissions for daemon services, delegated permissions for user-context apps180- Bot Framework for interactive scenarios — commands, dialogs, approval workflows181- Rate limits: Graph API allows 10,000 requests per 10 minutes per app per tenant182- Always handle `onMembersAdded` in bots — send welcome message with capabilities183- Store tokens securely — Azure Key Vault or environment variables, never in code184- Test Adaptive Cards at adaptivecards.io/designer before deploying185- Use Teams Toolkit (VS Code extension) for rapid development with templates186- For high-volume messaging, use batch requests (`$batch` endpoint) to reduce API calls