Creating a Microsoft Teams Bot: No-Code Automation with n8n and Make.com
Step-by-step guide to creating a Teams Bot without code.
Microsoft Teams has become indispensable in the workplace. But did you know you can create your own bots without programming? In this guide, we show you how to build Teams bots for notifications, interactions, and automation.
Why Build a Teams Bot?
Typical Use Cases:| Use Case | Description |
|---|---|
| Notifications | Server alerts, orders, tickets |
| Interactive Actions | Approvals, surveys, feedback |
| Information Retrieval | Query status, search data |
| Process Triggers | Start workflows from Teams |
- Directly in work context
- No additional app needed
- High user acceptance
Teams Messaging Options
1. Incoming Webhooks (Simplest Option)
For notifications only - no interaction.
Setup:// Node: HTTP Request
{
"method": "POST",
"url": "https://outlook.office.com/webhook/abc123...",
"headers": { "Content-Type": "application/json" },
"body": {
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"themeColor": "0076D7",
"summary": "New Order",
"sections": [{
"activityTitle": "Order #12345",
"facts": [
{ "name": "Customer", "value": "John Smith" },
{ "name": "Amount", "value": "$149.00" }
],
"markdown": true
}]
}
}
2. Outgoing Webhooks
Teams sends messages to your endpoint.
Setup:// Node: Webhook
// Teams sends on @Mention
{
"type": "message",
"text": "<at>BotName</at> What is the server status?",
"from": {
"name": "John Smith"
},
"channelId": "19:abc123..."
}
3. Bot Framework (Full-Featured)
For complex interactions - requires Azure Bot Service.
Workflow 1: Server Monitoring Alerts
The Workflow
Uptime Monitor (Server Down)
|
Teams Webhook:
"Server is unreachable!"
|
With action buttons:
[Acknowledge] [Escalate]
Adaptive Cards for Rich Messages
// Node: HTTP Request
{
"method": "POST",
"url": "{{ $env.TEAMS_WEBHOOK_URL }}",
"body": {
"type": "message",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"size": "Large",
"weight": "Bolder",
"text": "Server Alert",
"color": "Attention"
},
{
"type": "FactSet",
"facts": [
{ "title": "Server", "value": "{{ $json.server }}" },
{ "title": "Status", "value": "{{ $json.status }}" },
{ "title": "Time", "value": "{{ $now }}" }
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Open Dashboard",
"url": "https://monitoring.your-domain.com"
}
]
}
}
]
}
}
Workflow 2: Approval Workflow
The Workflow
New Vacation Request
|
Teams to Manager:
"John wants vacation from Jan 15-20"
[Approve] [Reject]
|
Manager clicks
|
n8n Webhook receives response
|
Update HR system
Notify employee
Adaptive Card with Actions
{
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"size": "Large",
"weight": "Bolder",
"text": "Vacation Request"
},
{
"type": "FactSet",
"facts": [
{ "title": "Employee", "value": "{{ $json.employeeName }}" },
{ "title": "From", "value": "{{ $json.startDate }}" },
{ "title": "To", "value": "{{ $json.endDate }}" },
{ "title": "Days", "value": "{{ $json.days }}" }
]
}
],
"actions": [
{
"type": "Action.Http",
"title": "Approve",
"method": "POST",
"url": "https://n8n.your-domain.com/webhook/vacation-approve",
"body": "{ \"requestId\": \"{{ $json.requestId }}\", \"action\": \"approve\" }"
},
{
"type": "Action.Http",
"title": "Reject",
"method": "POST",
"url": "https://n8n.your-domain.com/webhook/vacation-approve",
"body": "{ \"requestId\": \"{{ $json.requestId }}\", \"action\": \"reject\" }"
}
]
}
Webhook for Response
// Node: Webhook - Receives click
const { requestId, action } = $json;
if (action === 'approve') {
await updateHRSystem(requestId, 'approved');
await notifyEmployee(requestId, 'Your vacation has been approved!');
} else {
await updateHRSystem(requestId, 'rejected');
await notifyEmployee(requestId, 'Your vacation request was unfortunately rejected.');
}
// Update card
return {
statusCode: 200,
type: "application/json",
body: {
// Updated card
}
};
Workflow 3: Daily Summary
The Workflow
Schedule (daily 9:00 AM)
|
Collect data:
- Open tickets
- Today's meetings
- Upcoming deadlines
|
Teams: Send summary
Implementation
// Collect data
const tickets = await getOpenTickets();
const meetings = await getTodaysMeetings();
const deadlines = await getUpcomingDeadlines();
// Create Adaptive Card
const card = {
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"size": "Large",
"weight": "Bolder",
"text": "Good morning! Here is your daily briefing"
},
{
"type": "TextBlock",
"text": <strong>${tickets.length} open tickets</strong>,
"wrap": true
},
{
"type": "TextBlock",
"text": <strong>${meetings.length} meetings today</strong>,
"wrap": true
},
{
"type": "TextBlock",
"text": <strong>${deadlines.length} upcoming deadlines</strong>,
"wrap": true
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Open Dashboard",
"url": "https://dashboard.your-domain.com"
}
]
};
Workflow 4: Interactive FAQ Bot
With Power Virtual Agents (Low-Code)
With n8n (Custom)
Teams Outgoing Webhook
(User asks: "How do I request vacation?")
|
Analyze text (keywords)
|
Switch:
|-- "vacation" -> Vacation instructions
|-- "password" -> Password reset info
|-- "vpn" -> VPN instructions
+-- else -> "I didn't understand that"
|
Send response to Teams
Implementation
// Node: Code - FAQ Matching
const message = $json.text.toLowerCase();
const faqs = {
'vacation': 'Request vacation through the HR portal at hr.company.com/vacation',
'password': 'Reset password: IT Portal -> Self-Service -> Password',
'vpn': 'VPN access: Download the client from vpn.company.com',
'sick': 'Sick leave: Please email hr@company.com with your doctor\'s note'
};
let response = 'Sorry, I don\'t have information on that. Please contact IT Support.';
for (const [keyword, answer] of Object.entries(faqs)) {
if (message.includes(keyword)) {
response = answer;
break;
}
}
return { response };
Make.com: Teams Modules
Available Modules
Example Scenario
Microsoft Teams (Watch Messages)
|
Filter: Contains "help"
|
OpenAI: Generate response
|
Microsoft Teams: Reply
Graph API for Extended Functions
Send Direct Message
// Node: HTTP Request
{
"method": "POST",
"url": "https://graph.microsoft.com/v1.0/users/{{ $json.userId }}/chats/{{ $json.chatId }}/messages",
"headers": {
"Authorization": "Bearer {{ $json.accessToken }}",
"Content-Type": "application/json"
},
"body": {
"body": {
"content": "Hello! Here is an important message."
}
}
}
Create Chat
// Create 1:1 chat with user
{
"method": "POST",
"url": "https://graph.microsoft.com/v1.0/chats",
"body": {
"chatType": "oneOnOne",
"members": [
{
"@odata.type": "#microsoft.graph.aadUserConversationMember",
"roles": ["owner"],
"user@odata.bind": "https://graph.microsoft.com/v1.0/users/user1@company.com"
},
{
"@odata.type": "#microsoft.graph.aadUserConversationMember",
"roles": ["owner"],
"user@odata.bind": "https://graph.microsoft.com/v1.0/users/user2@company.com"
}
]
}
}
Message Formats
Simple Message
{
"text": "Hello, this is a simple message!"
}
With Markdown
{
"text": "<strong>Important:</strong> This is _formatted_ text with a link"
}
MessageCard (Legacy)
{
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"summary": "Summary",
"themeColor": "0076D7",
"title": "Message Title",
"sections": [
{
"facts": [
{ "name": "Fact 1", "value": "Value 1" }
]
}
],
"potentialAction": [
{
"@type": "OpenUri",
"name": "Learn More",
"targets": [
{ "os": "default", "uri": "https://example.com" }
]
}
]
}
Adaptive Card (Modern)
Adaptive Cards offer more flexibility:
- Input fields
- Dropdowns
- Date pickers
- Actions with HTTP callbacks
Best Practices
1. Don't Send Too Many Messages
// Batching: Collect multiple events
const events = await collectEvents(5 <em> 60 </em> 1000); // 5 min
if (events.length > 0) {
await sendDigest(events);
}
2. Clear Actionable Messages
// Bad
{ "text": "Something happened." }
// Good
{
"title": "Server webserver-01 is down",
"text": "Unreachable since 10:30 AM",
"actions": [
{ "title": "Dashboard", "url": "..." },
{ "title": "Logs", "url": "..." }
]
}
3. Error Handling
try {
await sendTeamsMessage(message);
} catch (error) {
if (error.status === 429) {
// Rate limit
await wait(60000);
await sendTeamsMessage(message);
} else {
// Fallback: Send email
await sendEmail(message);
}
}
Costs
| Solution | Cost |
|---|---|
| Incoming Webhooks | Free |
| Power Virtual Agents | From $200/month |
| Azure Bot Service | Pay-per-use |
| n8n/Make.com | From $9/month |
Conclusion
Microsoft Teams bots are a powerful tool:
- Notifications directly in work context
- Interactive workflows (approvals, etc.)
- FAQ bots for self-service
Next Steps
We can help you with Teams automation, from setup to a production-ready bot.