Skip to content
Learn Agentic AI
Learn Agentic AI10 min read5 views

Microsoft Teams AI Agent Integration: Bot Framework and Adaptive Cards

Build an AI agent for Microsoft Teams using the Bot Framework SDK, design rich Adaptive Card interfaces for structured interactions, and handle conversation flows with proper permissions and authentication.

Why Build AI Agents for Microsoft Teams

Microsoft Teams is the default collaboration platform for enterprises using Microsoft 365. An AI agent in Teams can automate approvals, answer policy questions, generate reports, and orchestrate cross-system workflows for millions of enterprise users without requiring them to leave their primary workspace.

The Bot Framework SDK provides a structured way to build conversational bots that work across Teams, with Adaptive Cards offering rich, interactive UI components that render natively in the Teams client.

Setting Up a Teams Bot

Register your bot in the Azure Bot Service, then use the Bot Framework SDK. The Python SDK uses an activity handler pattern where you override methods for different event types.

Hear it before you finish reading

Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.

Try Live Demo →
sequenceDiagram
    autonumber
    participant Caller as Caller
    participant Agent as CallSphere Agent
    participant API as CRM API
    participant DB as CRM Database
    participant Webhook as Webhook Listener
    Caller->>Agent: Inbound call begins
    Agent->>Agent: STT plus intent detection
    Agent->>API: Lookup contact by phone
    API->>DB: Read contact record
    DB-->>API: Contact and history
    API-->>Agent: Personalized context
    Agent->>API: Create call activity
    Agent->>API: Update deal stage
    API->>Webhook: Outbound webhook fires
    Webhook-->>Agent: Confirmed
    Agent->>Caller: Spoken confirmation
from botbuilder.core import (
    ActivityHandler, TurnContext, MessageFactory
)
from botbuilder.schema import Activity, Attachment
import json

class AIAgentBot(ActivityHandler):
    def __init__(self, agent_service):
        self.agent = agent_service

    async def on_message_activity(self, turn_context: TurnContext):
        user_message = turn_context.activity.text
        user_id = turn_context.activity.from_property.id

        # Send typing indicator while agent processes
        typing_activity = Activity(type="typing")
        await turn_context.send_activity(typing_activity)

        result = await self.agent.run(
            prompt=user_message,
            user_id=user_id,
        )

        await turn_context.send_activity(
            MessageFactory.text(result.answer)
        )

    async def on_members_added_activity(self, members_added, turn_context):
        for member in members_added:
            if member.id != turn_context.activity.recipient.id:
                await turn_context.send_activity(
                    "Hello! I am your AI assistant. "
                    "Ask me anything or type 'help' for options."
                )

Designing Adaptive Cards

Adaptive Cards are JSON-based UI templates that Teams renders natively. They support text, images, inputs, and action buttons — far richer than plain text responses.

def create_analysis_card(analysis: dict) -> Attachment:
    card_json = {
        "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
        "type": "AdaptiveCard",
        "version": "1.5",
        "body": [
            {
                "type": "TextBlock",
                "text": "Analysis Result",
                "size": "Large",
                "weight": "Bolder",
            },
            {
                "type": "FactSet",
                "facts": [
                    {"title": "Category", "value": analysis["category"]},
                    {"title": "Priority", "value": analysis["priority"]},
                    {"title": "Confidence", "value": f"{analysis['confidence']}%"},
                ],
            },
            {
                "type": "TextBlock",
                "text": analysis["summary"],
                "wrap": True,
            },
            {
                "type": "ActionSet",
                "actions": [
                    {
                        "type": "Action.Submit",
                        "title": "Approve",
                        "data": {
                            "action": "approve",
                            "analysis_id": analysis["id"]
                        },
                    },
                    {
                        "type": "Action.Submit",
                        "title": "Reject",
                        "data": {
                            "action": "reject",
                            "analysis_id": analysis["id"]
                        },
                    },
                ],
            },
        ],
    }

    return Attachment(
        content_type="application/vnd.microsoft.card.adaptive",
        content=card_json,
    )

Handling Card Submit Actions

When a user clicks a button on an Adaptive Card, Teams sends the action data back to your bot as a message activity with a value property.

async def on_message_activity(self, turn_context: TurnContext):
    activity = turn_context.activity

    # Check if this is a card action submission
    if activity.value:
        await self.handle_card_action(turn_context, activity.value)
        return

    # Regular text message
    await self.handle_text_message(turn_context)

async def handle_card_action(self, turn_context, action_data):
    action = action_data.get("action")
    analysis_id = action_data.get("analysis_id")

    if action == "approve":
        await self.agent.approve_analysis(analysis_id)
        await turn_context.send_activity(
            f"Analysis {analysis_id} approved and forwarded."
        )
    elif action == "reject":
        # Show rejection reason input card
        card = create_rejection_form_card(analysis_id)
        message = MessageFactory.attachment(card)
        await turn_context.send_activity(message)

Conversation State Management

Teams conversations can span channels, group chats, and 1:1 chats. Use the Bot Framework state management to persist context across turns.

from botbuilder.core import (
    ConversationState, UserState, MemoryStorage
)

storage = MemoryStorage()  # Use CosmosDB/Blob in production
conversation_state = ConversationState(storage)
user_state = UserState(storage)

class AIAgentBot(ActivityHandler):
    def __init__(self, agent_service, conv_state, usr_state):
        self.agent = agent_service
        self.conv_state = conv_state
        self.user_state = usr_state
        self.conv_accessor = conv_state.create_property("ConvData")
        self.user_accessor = usr_state.create_property("UserProfile")

    async def on_message_activity(self, turn_context):
        conv_data = await self.conv_accessor.get(turn_context, {})
        user_profile = await self.user_accessor.get(turn_context, {})

        history = conv_data.get("history", [])
        history.append({"role": "user", "content": turn_context.activity.text})

        result = await self.agent.run(
            prompt=turn_context.activity.text,
            history=history,
            user_prefs=user_profile,
        )

        history.append({"role": "assistant", "content": result.answer})
        conv_data["history"] = history[-20:]  # Keep last 20 turns

        await self.conv_accessor.set(turn_context, conv_data)
        await self.conv_state.save_changes(turn_context)

        await turn_context.send_activity(result.answer)

Permissions and Authentication

Teams apps require proper permission scoping in the app manifest. For AI agents, configure the minimum necessary permissions.

Still reading? Stop comparing — try CallSphere live.

CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.

# Validate that the user has permission for the requested action
async def check_user_permission(turn_context, required_role):
    user_id = turn_context.activity.from_property.aad_object_id
    member = await turn_context.activity.get_member(user_id)

    user_roles = await get_roles_from_directory(user_id)

    if required_role not in user_roles:
        await turn_context.send_activity(
            f"You need the '{required_role}' role for this action."
        )
        return False
    return True

FAQ

How do I deploy a Teams bot to production?

Register the bot in Azure Bot Service, deploy your Python application to Azure App Service or a container, and configure the messaging endpoint URL. Then create a Teams app package (manifest.json plus icons) and upload it to your organization's Teams app catalog through the Teams admin center.

Can Adaptive Cards collect user input like forms?

Yes. Adaptive Cards support Input.Text, Input.ChoiceSet (dropdowns), Input.Date, Input.Toggle, and more. When paired with Action.Submit, the card sends all input values as a JSON object in the activity's value property, which your bot processes like any card action.

What is the message size limit for Teams bot responses?

Teams limits individual messages to about 28 KB of text. For Adaptive Cards, the payload limit is 40 KB. If your AI agent generates large responses, split them across multiple messages or summarize and offer a "view full report" link to an external page.


#MicrosoftTeams #BotFramework #AdaptiveCards #AIAgents #EnterpriseIntegration #AgenticAI #LearnAI #AIEngineering

Share

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.

Related Articles You May Like

AI Strategy

AI Agent M&A Activity 2026: Aircall–Vogent, Meta–PlayAI, OpenAI's Six Deals

Q1 2026 saw a record acquisition wave: Aircall bought Vogent (May), Meta acquired Manus and PlayAI, OpenAI closed six deals. The voice AI consolidation phase has begun.

Agentic AI

LangGraph State-Machine Architecture: A Principal-Engineer Deep Dive (2026)

How LangGraph's StateGraph, channels, and reducers actually work — with a working multi-step agent, eval hooks at every node, and the patterns that survive production.

Agentic AI

LangGraph Checkpointers in Production: Durable, Resumable Agents with Eval Replay

Use LangGraph's checkpointer to make agents resumable across crashes and human-in-the-loop pauses, then replay any checkpoint into your eval pipeline.

Agentic AI

Multi-Agent Handoffs with the OpenAI Agents SDK: The Pattern That Actually Scales (2026)

Handoffs done right — when one agent should hand control to another, how to preserve context, and how to evaluate the handoff decision itself.

Agentic AI

Building Your First Agent with the OpenAI Agents SDK in 2026: A Hands-On Walkthrough

Step-by-step build of a working agent with the OpenAI Agents SDK — Agent class, tools, handoffs, tracing — plus an eval pipeline that catches regressions before merge.

Agentic AI

LangGraph Supervisor Pattern: Orchestrating Multi-Agent Teams in 2026

The supervisor pattern in LangGraph for coordinating specialist agents, with full code, an eval pipeline that scores routing accuracy, and the failure modes to watch for.