Edit

Channel and group chat conversations for agents

To enable users to install an agent in a team or group chat, add the teams or groupchat scope. This allows all members of the conversation to interact with your agent. After the agent is installed, it has access to metadata about the conversation, such as the list of conversation members. Also, when it's installed in a team, the agent has access to details about that team and the full list of channels.

By default, agents in group chats and channels only receive messages when they're directly @mentioned. They don't receive other messages sent to the conversation. For example, your agent doesn't receive a message when the team or channel is mentioned, or when someone replies to a message from your agent without @mentioning it. The Teams SDK provides a dedicated mention activity route to handle @mention events.

Note

  • Using resource-specific consent (RSC), an agent can receive all channel and group chat messages in conversations where it's installed without being @mentioned. For more information, see receive all messages for agents.
  • Private channel support for agent apps is limited. You can add agent-enabled apps in private channels where private channel app support is enabled, but agents can't post messages or Adaptive Cards in private channel conversations. For private and shared channel app support details, see apps for shared and private channels.

Design guidelines

In group chats and channels, design your agent for collaborative conversations with clear value, concise responses, and minimal noise.

Threaded conversations

In Teams channels, messages can be organized into threads. When your agent receives a message in a thread, the conversation context already carries the thread ID. Use Send() to send a message in the same thread without quoting, or Reply() to send with a visual quote of the inbound message.

app.OnMessage(async (context, cancellationToken) =>
{
    // Send in the same thread, no quote
    await context.Send("Acknowledged", cancellationToken);

    // Send in the same thread with a visual quote of the inbound message
    await context.Reply("Got it!", cancellationToken);
});

When your agent receives a message in a thread, the conversation context already carries the thread ID. Use send() to send a message in the same thread without quoting, or reply() to send with a visual quote of the inbound message.

app.on('message', async ({ send, reply }) => {
  // Send in the same thread, no quote
  await send('Acknowledged');

  // Send in the same thread with a visual quote of the inbound message
  await reply('Got it!');
});

When your agent receives a message in a thread, the conversation context already carries the thread ID. Use send() to send a message in the same thread without quoting, or reply() to send with a visual quote of the inbound message.

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # Send in the same thread, no quote
    await ctx.send("Acknowledged")

    # Send in the same thread with a visual quote of the inbound message
    await ctx.reply("Got it!")

For sending messages into a thread proactively, see Proactive messages.

Send a message on installation

When your agent is first added to a group or team, you can send an introduction message by using the install.add lifecycle route. For more information, see proactive messaging.

If you send an introduction message, include a brief description of the agent's features and how to use them.

You can also store the conversationId during installation to enable proactive messaging later.

The following code shows an example of sending welcome messages on installation:

app.OnInstall(async context => 
{ 
    await context.Send("Hello! I'm your agent. Here's what I can do..."); 
}); 
app.on('install.add', async ({ send }) => 
{ 
    await send('Hello! I\'m your agent. Here\'s what I can do...'); 
}); 
@app.on_install_add 
async def handle_install_add(ctx: ActivityContext[InstalledActivity]): 
    await ctx.send("Hello! I'm your agent. Here's what I can do...") 

Don't send proactive welcome messages to users individually when the agent is installed in a team or group chat. If you send a welcome message, post it in the installed conversation and mention the person who added the agent.

Note

Ensure that the message sent by the agent is relevant and adds value to the initial message and doesn't spam the users.

Don't send a message in the following cases:

  • When the team is large, for example, larger than 100 members. Your agent can be seen as spam and the person who added it can get complaints. You must clearly communicate your agent's value proposition to everyone who sees the welcome message.
  • Your agent is first mentioned in a group or channel instead of being first added to a team.
  • A group or channel is renamed.
  • A team member is added to a group or channel.

Work with mentions

In group chats and channels, messages that @mention your agent include a mention entity in the message text. If your agent is configured to receive all messages, such as with RSC, some incoming messages might not include an @mention. Your agent can retrieve other users mentioned in a message and add mentions to messages it sends. Agents in group chats enable user mentions using @mention; however, they don’t support @everyone for mentions.

For messages that include @mentions, the message text contains mention markup such as <at>@agentname</at>.

Retrieve mentions

Mentions are returned in the entities object in the activity payload and contain both the unique ID of the user and the name of the user mentioned. The text of the message also includes the mention, such as <at>@John Smith<at>. However, don't rely on the text in the message to retrieve any information about the user. It's possible for the person sending the message to alter it. Therefore, use the entities object.

You can retrieve all mentions in the message by filtering the entities array in the activity for entries with type set to mention.

The following code shows an example of retrieving mentions:

app.OnMessage(async context =>
{
    var mentions = context.Activity.Entities?
        .Where(e => e.Type == "mention")
        .ToList();

    if (mentions != null && mentions.Any())
    {
        var firstMention = mentions[0].Properties["mentioned"]?["name"]?.ToString();
        await context.Send($"Hello {firstMention}");
    }
    else
    {
        await context.Send("Aw, no one was mentioned.");
    }
});
app.on('message', async ({ activity, send }) => {
    const mentions = activity.entities?.filter(e => e.type === 'mention');

    if (mentions && mentions.length > 0) {
        const firstMention = mentions[0].mentioned;
        await send(`Hello ${firstMention.name}.`);
    } else {
        await send('Aw, no one was mentioned.');
    }
});
@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    mentions = [e for e in (ctx.activity.entities or []) if e.type == "mention"]

    if mentions:
        first_mention = mentions[0].mentioned
        await ctx.send(f"Hello {first_mention.name}")
    else:
        await ctx.send("Aw, no one was mentioned.")
{
    "type": "message",
    "text": "Hey <at>Pranav Smith</at> check out this message",
    "timestamp": "2017-10-29T00:51:05.9908157Z",
    "localTimestamp": "2017-10-28T17:51:05.9908157-07:00",
    "serviceUrl": "https://skype.botframework.com",
    "channelId": "msteams",
    "from": {
        "id": "29:9e52142b-5e5e-4d7b-bb3e-e82dcf620000",
        "name": "Jane Smith"
    },
    "conversation": {
        "id": "19:aebd0ad4d6ab42c8b9ed19c251c2fc37@thread.skype;messageid=1481567603816"
    },
    "recipient": {
        "id": "8:orgid:6aebbad0-e5a5-424a-834a-20fb051f3c1a",
        "name": "stlrgload100"
    },
    "attachments": [
        {
            "contentType": "image/png",
            "contentUrl": "https://upload.wikimedia.org/wikipedia/en/a/a6/Bender_Rodriguez.png",
            "name": "Bender_Rodriguez.png"
        }
    ],
    "entities": [
        {
            "type":"mention",
            "mentioned":{
                "id":"29:08q2j2o3jc09au90eucae",
                "name":"Pranav Smith"
            },
            "text": "<at>@Pranav Smith</at>"
        }
    ],
    "replyToId": "3UP4UTkzUk1zzeyW"
}

Check for and strip @mention

In channels and group chats, users typically address an agent or app with an @mention. Before interpreting the message, check that the mention targets your agent or app, then remove the mention text and trim whitespace. This leaves only the user’s command or prompt for processing.

Removing the mention prevents the agent or app name from interfering with command matching, intent recognition, search, or natural-language processing. It also lets the same handler process messages consistently across personal chats, group chats, and channels. Preserve other mentions when they are part of the user’s request.

Note

The TypeScript and Python versions for Teams SDK include built-in functions to remove @mention.

string StripMentions(MessageActivity msg)
{
    var text = msg.Text ?? "";
    if (msg.Entities == null) return text;

    foreach (var entity in msg.Entities)
    {
        if (entity is MentionEntity mention && mention.Text != null)
        {
            text = text.Replace(mention.Text, "");
        }
    }

    return text.Trim();
}

This code snippet demonstrates how to clean a Teams message before command parsing:

  • msg.Entities contains structured metadata such as mentions.
  • Replace(mention.Text, "") removes the visible mention such as @contoso from the message.
  • Trim() removes leftover spaces.

For example, @contoso summarize this thread becomes summarize this thread.

The function removes all mentions, not only the agent's or bot’s mention. If other mentions are meaningful input, verify that a mention refers to the current bot before removing it.

app.on('message', async ({ activity, send }) => {
  const clean = activity.stripMentionsText().text;
  await send(`You said: ${clean}`);
});

This code snippet demonstrates listening for incoming message activities and removing the @mention text before processing the user’s message.

  • activity.stripMentionsText() removes mention text such as @contoso from the activity.
  • .text retrieves the cleaned message content.
  • send() echoes the cleaned text back to the user.

For example, @contoso summarize this chat becomes summarize this chat, so the agent can parse the command.

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    clean = ctx.activity.strip_mentions_text().text
    await ctx.send(f"You said: {clean}")

This code snippets shows how to listen for incoming messages and remove @mention text before processing them.

  • ctx.activity.strip_mentions_text() removes mention text such as @contoso.
  • .text returns the cleaned message.
  • ctx.send() replies with the cleaned text.

For example, @contoso summarize this chat becomes summarize this chat, making the message easier to parse as a command or prompt.

Add mentions to your messages

Your agent can mention other users in messages posted in channels. To include a mention inline in your message, place the mention in the message text and add the mention details to the entities array. The text field in the mention entity must match the exact text in the message body.

The following code shows an example of adding mentions to your messages:

app.OnMessage(async context =>
{
    var user = context.Activity.From;
    var message = new MessageActivity($"Hello <at>{user.Name}</at>!").AddMention(user);
    await context.Send(message);
});
app.on('message', async ({ send, activity }) => {
    const user = activity.from;
    const message = new MessageActivity(`Hello <at>${user.name}</at>!`).addMention(user);
    await send(message);
});
@app.on_message 
async def handle_message(ctx: ActivityContext[MessageActivity]): 
    await ctx.send(MessageActivityInput(text="Hello!").add_mention(account=ctx.activity.from_))
{
    "type": "message",
    "text": "Hey <at>Pranav Smith</at> check out this message",
    "timestamp": "2017-10-29T00:51:05.9908157Z",
    "localTimestamp": "2017-10-28T17:51:05.9908157-07:00",
    "serviceUrl": "https://skype.botframework.com",
    "channelId": "msteams",
    "from": {
        "id": "29:9e52142b-5e5e-4d7b-bb3e-e82dcf620000",
        "name": "Jane Smith"
    },
    "conversation": {
        "id": "19:aebd0ad4d6ab42c8b9ed19c251c2fc37@thread.skype;messageid=1481567603816"
    },
    "recipient": {
        "id": "8:orgid:6aebbad0-e5a5-424a-834a-20fb051f3c1a",
        "name": "stlrgload100"
    },
    "attachments": [
        {
            "contentType": "image/png",
            "contentUrl": "https://upload.wikimedia.org/wikipedia/en/a/a6/Bender_Rodriguez.png",
            "name": "Bender_Rodriguez.png"
        }
    ],
    "entities": [
        {
            "type":"mention",
            "mentioned":{
                "id":"29:08q2j2o3jc09au90eucae",
                "name":"Pranav Smith"
            },
            "text": "<at>@Pranav Smith</at>"
        }
    ],
    "replyToId": "3UP4UTkzUk1zzeyW"
}

You can also mention users by their Microsoft Entra Object ID or User Principal Name (UPN), and mention tags in channel messages.

Support for Microsoft Entra Object ID and UPN in user mention

Bots can mention users by Microsoft Entra Object ID or User Principal Name (UPN), in addition to user IDs. Incoming Webhooks also support user mentions in Adaptive Cards using these ID types.

The following code snippet shows an example of mentioning users with Entra Object ID and UPN using the Teams SDK:

app.OnMessage(async context =>
{
    // Mention a user by their User Principal Name (UPN)
    var user = new Account { Id = "Adele@microsoft.com", Name = "Adele" };
    await context.Send(new MessageActivity("Hello!").AddMention(user));
});
app.on('message', async ({ send }) => {
    // Mention a user by their User Principal Name (UPN)
    const user = { id: 'Adele@microsoft.com', name: 'Adele' };
    await send(new MessageActivity('Hello!').addMention(user));
});
from microsoft_teams.api import Account, MessageActivityInput

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # Mention a user by their User Principal Name (UPN)
    user = Account(id="Adele@microsoft.com", name="Adele")
    await ctx.send(MessageActivityInput(text="Hello!").add_mention(account=user))
{
    "type": "mention",
    "text": "<at>Adele</at>",
    "mentioned": {
            "id": "Adele@microsoft.com",
            "name": "Adele"
    }
}

Tag mention

Your agent can mention tags in text messages and Adaptive Cards posted in channels. When the agent @mentions the tag in a channel, the tag is highlighted and the people associated with the tag get notified. When a user hovers over the tag, a pop-up appears with the tag details.

Note

Tag mentions aren't supported in Teams operated by 21Vianet.

Mention tags in a text message

To mention a tag, include a mention entity with "type": "tag" in your message. The id field must be the base64-encoded tag ID from the List teamworkTags API.

app.OnMessage(async context =>
{
    // Mention a tag using the tag's Graph API ID
    var tag = new Account { Id = "<base64-encoded-tag-id>", Name = "Test Tag" };
    await context.Send(new MessageActivity("Hello!").AddMention(tag));
});
app.on('message', async ({ send }) => {
    // Mention a tag using the tag's Graph API ID
    const tag = { id: '<base64-encoded-tag-id>', name: 'Test Tag' };
    await send(new MessageActivity('Hello!').addMention(tag));
});
@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # Mention a tag using the tag's Graph API ID
    tag = Account(id="<base64-encoded-tag-id>", name="Test Tag")
    await ctx.send(MessageActivityInput(text="Hello!").add_mention(account=tag))

Note

When mentioning tags, the underlying wire format requires the "type": "tag" property in the mentioned object of the entity. If the "type": "tag" property isn't included, the agent treats the mention as a user mention.

Mention tags in an Adaptive Card

In the Adaptive Card schema, under the mentioned object, add the "type": "tag" property. If the "type": "tag" property isn't added, the agent treats the mention as a user mention.

You can get the list of the tags available in the channel using the List teamworkTags API.

Example:

{
    "type": "mention",
    "text": "<at>Test Tag</at>",
    "mentioned": {
            "id": "base64 encoded id",
            "name": "Test Tag",
            "type": "tag"
    }
}
Query Parameters
Name Description
type The type of mention. The supported type is tag.
id The unique identifier for the tag. For more information, see teamworkTag.
Error code
Status code Error code Message values Retry request Developer action
400 Code: Bad Request Mentioned tag with ID {id string} doesn't exist in current team
Tag can only be mentioned in channel
Invalid mentioned tag because no tag exists in the team
No Reevaluate request payload for errors. Check returned error message for details.
502 Code: Bad Gateway Invalid team group ID
Malformed tenant ID for the tag
Mention ID can't be resolved
No Retry manually.
Throttling limits

Any request can be evaluated against multiple limits, depending on the scope, the window type (short and long), number of tags per message, and other factors. The first limit to be reached triggers throttling behavior.

Ensure that you don't exceed the throttling limits to avoid failed message delivery. For example, an agent can send only two messages with tag mention in a five-second window and each message can have only up to 10 tags.

The following table lists the throttling limits for tag mentions in an agent:

Scope Window Type Number of tags per message Time windows (sec) Maximum number of messages per time window
Per agent per thread Short 10 5 2
  Long 10 60 5
All agents per thread Short 10 5 4
  Long 10 60 5
Limitations
  • Tag mentions are supported only in agent to client message flow with text and Adaptive Card.
  • Tag mentions aren't supported in shared and private channels.
  • Tag mentions aren't supported in connectors.
  • Tag mentions don't support the invoke flow in an agent.

Next step

See also