Messages in bot conversations

Each message in a conversation is an Activity object of type messageType: message. When a user sends a message, Microsoft Teams posts the message activity to your bot. Teams sends a JSON object to your bot's messaging endpoint and Teams allows only one endpoint for messaging. Your bot examines the message to determine its type and responds accordingly.

Basic conversations are handled through the Bot Framework connector, a single REST API. This API enables your bot to communicate with Teams and other channels. The Bot Builder SDK provides the following features:

  • Easy access to the Bot Framework connector.
  • Functionality to manage conversation flow and state.
  • Simple ways to incorporate cognitive services, such as natural language processing (NLP).

Your bot receives messages from Teams using the Text property and it sends single or multiple message responses to the users.

For more information, see user attribution for bot messages.

The following table lists the activity that your bot can receive and take action on:

Type Payload object Scope
Receive a message activity Message activity All
Receive edit message activity Message edit activity All
Receive undelete message activity Message undelete activity All
Receive soft delete message activity Message soft delete activity All

Receive a message activity

To receive a text message, use the Text property of an Activity object. In the bot's activity handler, use the turn context object's Activity to read a single message request.

The following code shows an example of receiving a message activity:


protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
  // Sends an activity to the sender of the incoming activity.
  await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken);
}

Receive a read receipt

The Read receipts setting in Teams allow the sender of a chat message to be notified when their message was read by the recipient in one-on-one and group chats. After the recipient reads the message, the Seen appears next to the message. You also have the option to configure your bot to receive read receipt events through the Read receipts setting. The read receipt event helps you enhance user experience in the following ways:

  • You can configure your bot to send a follow-up message if your app user hasn't read the message in the personal chat.

  • You can create a feedback loop using read receipts to tune your bot’s experience.

Note

  • Read receipts are supported only in user to bot chat scenarios.
  • Read receipts for bots doesn’t support team, channel, and group chat scopes.
  • If a tenant admin or user disables the Read receipts setting, the bot doesn't receive the read receipt event.

To receive read receipts events for your bot, ensure the following:

  • Add the RSC ChatMessageReadReceipt.Read.Chat permission in the app manifest, as follows:
    
"webApplicationInfo": {
    
     "id": "38f0ca43-1c38-4c39-8097e-47f62c686500",
     "resource": ""
},
"authorization": {
    "permissions": {
    "orgwide": [],
     "resourceSpecific": [
        {
        "name": "ChatMessageReadReceipt.Read.Chat",
        "type": "Application"
        }
        ]
     }
 }
    

You can also add RSC permissions through Graph API. For more information, see consentedPermissionSet.

  • Override the method OnTeamsReadReceiptAsync with IsMessageRead handler.

    The IsMessageRead helper method is useful to determine if the message is read by the recipients. If the compareMessageId is less than or equal to the LastReadMessageId, then the message has been read. Override the OnTeamsReadReceiptAsync method to receive read receipts with IsMessageRead helper method:

    
    protected override async Task OnTeamsReadReceiptAsync(ReadReceiptInfo readReceiptInfo, ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken) 
    {
        var lastReadMessageId = readReceiptInfo.LastReadMessageId;
       if (IsMessageRead("{id of the message that you care}", LastReadMessageId))
       {
            await turnContext.SendActivityAsync(MessageFactory.Text("User read the bot's message"), cancellationToken);    
        }
    }
    

    Following is an example of read receipts event request that a bot receives:

    {
        "name": "application/vnd.microsoft.readReceipt",
        "type": "event",
        "timestamp": "2023-08-16T17:23:11.1366686Z",
        "id": "f:b4783e72-9d7b-2ed9-ccef-ab446c873007",
        "channelId": "msteams",
        "serviceUrl": "https://smba.trafficmanager.net/amer/",
        "from": {
            "id": "29:1-8Iuh70W9pRqV8tQK8o2nVjxz33RRGDKLf4Bh7gKnrzN8s7e4vCyrFwjkPbTCX_Co8c4aXwWvq3RBLr-WkkVMw",
            "aadObjectId": "5b649834-7412-4cce-9e69-176e95a394f5"
        },
        "conversation": {
            "conversationType": "personal",
            "tenantId": "6babcaad-604b-40ac-a9d7-9fd97c0b779f",
            "id": "a:1xlimp68NSUxEqK0ap2rXuwC9ITauHgV2M4RaDPkeRhV8qMaFn-RyilMZ62YiVdqs8pp43yQaRKvv_U2S2gOS5nM-y_pOxVe4BW1qMGPtqD0Bv3pw-nJXF0zhDlZHMZ1Z"
        },
        "recipient": {
            "id": "28:9901a8b6-4fef-428b-80b1-ddb59361adeb",
            "name": "Test Bot"
        },
        "channelData": {
            "tenant": {
                "id": "6babcaad-604b-40ac-a9d7-9fd97c0b779f"
            }
        },
        "value": {
            "lastReadMessageId": "1692206589131"
        }
    }
    
    
  • Read receipt admin setting or user setting is turned on for the tenant for the bot to receive the read receipt events. The tenant admin or the user must enable or disable the read receipt setting.

After the bot is enabled in a user to bot chat scenario, the bot promptly receives a read receipt event when the user reads the bot's message. You can track the user engagement by counting the number of events and you can also send a context aware message.

Send a message

To send a text message, specify the string you want to send as an activity. In the bot's activity handler, use the turn context object's SendActivityAsync method to send a single message response. Use the object's SendActivitiesAsync method to send multiple responses.

The following code shows an example of sending a message when a user is added to a conversation:


protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
  // Sends an activity to the sender of the incoming activity.
  await turnContext.SendActivityAsync(MessageFactory.Text($"Hello and welcome!"), cancellationToken);
}

Note

  • Message splitting occurs when a text message and an attachment are sent in the same activity payload. Teams splits this activity into two separate activities, one with a text message and the other with an attachment. As the activity is split, you do not receive the message ID in response, which is used to update or delete the message proactively. It is recommended to send separate activities instead of depending on message splitting.
  • Messages sent can be localized to provide personalization. For more information, see localize your app.

Messages sent between users and bots include internal channel data within the message. This data allows the bot to communicate properly on that channel. The Bot Builder SDK allows you to modify the message structure.

Get edit message activity

When you edit a message, the bot gets a notification of the edit message activity.

To get an edit message activity notification in a bot, you can override OnTeamsMessageEditAsync handler.

Following is an example of an edit message activity notification using OnTeamsMessageEditAsync when a sent message is edited:


protected override async Task OnTeamsMessageEditAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken) 
{ 
var replyActivity = MessageFactory.Text("message is updated"); 
await turnContext.SendActivityAsync(replyActivity, cancellationToken); 
} 

Get undelete message activity

When you undelete a message, the bot gets a notification of the undelete message activity.

To get an undelete message activity notification in a bot, you can override OnTeamsMessageUndeleteAsync handler.

The following is an example of an undelete message activity notification using OnTeamsMessageUndeleteAsync when a deleted message is restored:


protected override async Task OnTeamsMessageUndeleteAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
{ 
var replyActivity = MessageFactory.Text("message is undeleted"); 
await turnContext.SendActivityAsync(replyActivity, cancellationToken); 
} 

Get soft delete message activity

When you soft delete a message, the bot gets a notification of the soft delete message activity.

To get a soft delete message activity notification in a bot, you can override OnTeamsMessageSoftDeleteAsync handler.

Following is an example of a soft delete message activity notification using OnTeamsMessageSoftDeleteAsync when a message is soft deleted:


protected override async Task OnTeamsMessageSoftDeleteAsync(ITurnContext<IMessageDeleteActivity> turnContext, CancellationToken cancellationToken) 
{ 
var replyActivity = MessageFactory.Text("message is soft deleted"); 
await turnContext.SendActivityAsync(replyActivity, cancellationToken); 
} 

Send suggested actions

Suggested actions help users with ideas of what to ask next, based on the previous response or conversation. Your bot should offer context-specific suggestions to the user, rather than generic or fixed ones. You can use your bot’s large language model (LLM) to generate up to three suggestions along with its responses. Then, you can extract these suggestions and present them as options for the user to choose.

When a user selects a button, it remains visible and accessible on the rich cards. However, for suggested actions, the buttons are designed to disappear after selection to prevent the user from selecting stale options that may no longer be relevant.

Note

  • SuggestedActions are only supported for one-on-one chat bots with both text based messages and Adaptive Cards.
  • SuggestedActions aren't supported for chat bots with attachments for any conversation type.
  • imBack is the only supported action type and Teams display up to three suggested actions.

To add suggested actions to a message, specify a list of card action objects that represent the buttons to be displayed to the user for the sugestedActions property of the activity object.

The following is an example to implement and experience suggested actions:

{
  "type": "message",
  "from": {
    "id": "12345678",
    "name": "sender's name"
  },
  "conversation": {
    "id": "abcd1234",
    "name": "conversation's name"
  },
  "recipient": {
    "id": "1234abcd",
    "name": "recipient's name"
  },
  "text": "What are the tasks for the day.",
  "inputHint": "expectingInput",
  "suggestedActions": {
    "actions": [
      {
        "type": "imBack",
        "title": "Create a new query identifying overdue tasks",
        "value": "Create a new query identifying overdue tasks"
      },
      {
        "type": "imBack",
        "title": "Create a new work item for this feature",
        "value": "Create a new work item for this feature"
            }
        ]
    },
  "replyToId": "5d5cdc723"
}

The following illustrates an example of suggested actions:

Teams channel data

The channelData object contains Teams-specific information and is a definitive source for team and channel IDs. Optionally, you can cache and use these IDs as keys for local storage. The TeamsActivityHandler in the SDK pulls out important information from the channelData object to make it accessible. However, you can always access the original data from the turnContext object.

The channelData object isn't included in messages in personal conversations, as these take place outside of a channel.

A typical channelData object in an activity sent to your bot contains the following information:

  • eventType: Teams event type passed only in cases of channel modification events.
  • tenant.id: Microsoft Entra tenant ID passed in all contexts.
  • team: Passed only in channel contexts, not in personal chat.
    • id: GUID for the channel.
    • name: Name of the team passed only in cases of team rename events.
  • channel: Passed only in channel contexts, when the bot is mentioned or for events in channels in teams, where the bot is added.
  • channelData.teamsTeamId: Deprecated. This property is only included for backward compatibility.
  • channelData.teamsChannelId: Deprecated. This property is only included for backward compatibility.

Example channelData object

The following code shows an example of channelData object (channelCreated event):

"channelData": {
    "eventType": "channelCreated",
    "tenant": {
        "id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
    },
    "channel": {
        "id": "19:[email protected]",
        "name": "My New Channel"
    },
    "team": {
        "id": "19:[email protected]"
    }
}

Message content

Messages received from or sent to your bot can include different types of message content.

Format From user to bot From bot to user Notes
Rich text ✔️ ✔️ Your bot can send rich text, pictures, and cards. Users can send rich text and pictures to your bot.
Pictures ✔️ ✔️ Maximum 1024 × 1024 pixels and 1 MB in PNG, JPEG, or GIF format. Doesn't support the animated GIF.
Cards ✔️ See Teams card reference for supported cards.
Emojis ✔️ ✔️ Teams supports emojis through UTF-16, such as U+1F600 for grinning face.

Picture messages

To enhance your message, you can include pictures as attachments to that message. For more information on attachments, see add media attachments to messages.

Pictures can be at most 1024 × 1024 pixels and 1 MB in PNG, JPEG, or GIF format. Animated GIF isn't supported.

Specify the height and width of each image by using XML. In Markdown, the image size defaults to 256×256. For example:

  • Use: <img src="http://aka.ms/Fo983c" alt="Duck on a rock" height="150" width="223"></img>.
  • Don't use: ![Duck on a rock](http://aka.ms/Fo983c).

A conversational bot can include Adaptive Cards that simplify business workflows. Adaptive Cards offer rich customizable text, speech, images, buttons, and input fields.

Adaptive Cards

Adaptive Cards can be authored in a bot and shown in multiple apps such as Teams, your website, and so on. For more information, see Adaptive Cards.

The following code shows an example of sending a simple Adaptive Card:

{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
    {
        "items": [
        {
            "size": "large",
            "text": " Simple Adaptivecard Example with a Textbox",
            "type": "TextBlock",
            "weight": "bolder",
            "wrap": true
        },
        ],
        "spacing": "extraLarge",
        "type": "Container",
        "verticalContentAlignment": "center"
    }
    ]
}

Add notifications to your message

There are two ways to send a notification from your application:

  • By setting the Notification.Alert property on bot message.
  • By sending an activity feed notification using the Graph API.

You can add notifications to your message using the Notification.Alert property. Notifications alert users to an event in your application such as new tasks, mentions, or comments. These alerts are related to what users are working on or what they must look at by inserting a notice into their activity feed. For notifications to trigger from your bot message, set the TeamsChannelData objects Notification.Alert property to true. If a notification is raised depends on the individual user's Teams settings, and you can't override these settings.

If you want to generate an arbitrary notification without sending a message to the user, then you can use the Graph API. For more information, see how to send activity feed notifications using Graph API along with the best practices.

Note

The Summary field displays any text from the user as a notification message in the feed.

The following code shows an example of adding notifications to your message:

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
  // Returns a simple text message.
  var message = MessageFactory.Text("You'll get a notification, if you've turned them on.");
  message.TeamsNotifyUser();

  // Sends an activity to the sender of the incoming activity.
  await turnContext.SendActivityAsync(message);
}

Status codes from bot conversational APIs

Ensure to handle these errors appropriately in your Teams app. The following table lists the error codes and the descriptions under which the errors are generated:

Status code Error code and message values Description Retry request Developer action
400 Code: Bad Argument
Message: *scenario specific
Invalid request payload provided by the bot. See error message for specific details. No Reevaluate request payload for errors. Check returned error message for details.
401 Code: BotNotRegistered
Message: No registration found for this bot.
The registration for this bot wasn't found. No Verify the bot ID and password. Ensure the bot ID (Microsoft Entra ID) is registered in the Teams Developer Portal or via Azure bot channel registration in Azure with 'Teams' channel enabled.
403 Code: BotDisabledByAdmin
Message: The tenant admin disabled this bot
Tenant admin blocked interactions between user and the bot app. Tenant admin needs to allow the app for the user inside of app policies. For more information, see app policies. No Stop posting to conversation until interaction with bot is explicitly initiated by a user in the conversation indicating that the bot is no longer blocked.
403 Code: BotNotInConversationRoster
Message: The bot isn't part of the conversation roster.
The bot isn't part of the conversation. App needs to be reinstalled in conversation. No Before attempting to send another conversation request, wait for an installationUpdate event, which indicates that the bot is added again.
403 Code: ConversationBlockedByUser
Message: User blocked the conversation with the bot.
User blocked the bot in personal chat or a channel through moderation settings. No Delete the conversation from cache. Stop attempting to post to conversations until interaction with bot is explicitly initiated by a user in the conversation, indicating that the bot is no longer blocked.
403 Code: ForbiddenOperationException
Message: Bot isn't installed in user's personal scope
Proactive message is sent by a bot, which isn't installed in a personal scope. No Before attempting to send another conversation request, install the app in personal scope.
403 Code: InvalidBotApiHost
Message: Invalid bot api host. For GCC tenants, call https://smba.infra.gcc.teams.microsoft.com.
The bot called the public API endpoint for a conversation that belongs to a GCC tenant. No Update the service URL for the conversation to https://smba.infra.gcc.teams.microsoft.com and retry the request.
403 Code: NotEnoughPermissions
Message: *scenario specific
Bot doesn't have required permissions to perform the requested action. No Determine the required action from the error message.
404 Code: ActivityNotFoundInConversation
Message: Conversation not found.
The message ID provided couldn't be found in the conversation. Message doesn't exist or it is deleted. No Check if message ID sent is an expected value. Remove the ID if it was cached.
404 Code: ConversationNotFound
Message: Conversation not found.
Conversation wasn't found as it doesn't exist or is deleted. No Check if conversation ID sent is an expected value. Remove the ID if it was cached.
412 Code: PreconditionFailed
Message: Precondition failed, please try again.
A precondition failed on one of our dependencies due to multiple concurrent operations on the same conversation. Yes Retry with exponential backoff.
413 Code: MessageSizeTooBig
Message: Message size too large.
The size of the incoming request was too large. For more information, see format your bot messages. No Reduce the payload size.
429 Code: Throttled
Message: Too many requests. Also returns when to retry after.
Too many requests sent by the bot. For more information, see rate limit. Yes Retry using Retry-After header to determine backoff time.
500 Code: ServiceError
Message: *various
Internal server error. No Report the issue in developer community.
502 Code: ServiceError
Message: *various
Service dependency issue. Yes Retry with exponential backoff. If the issue persists, report the issue in developer community.
503 Service is unavailable. Yes Retry with exponential backoff. If the issue persists, report the issue in developer community.
504 Gateway Timeout. Yes Retry with exponential backoff. If the issue persists, report the issue in developer community.

Status codes retry guidance

The general retry guidance for each status code is listed in the following table, bot must avoid retrying status codes that aren't specified:

Status code Retry strategy
403 Retry by calling the GCC API https://smba.infra.gcc.teams.microsoft.com for InvalidBotApiHost.
412 Retry using exponential backoff.
429 Retry using Retry-After header to determine the wait time in seconds and in between requests, if available. Otherwise, retry using exponential backoff with thread ID, if possible.
502 Retry using exponential backoff.
503 Retry using exponential backoff.
504 Retry using exponential backoff.

Code sample

Sample name Description Node.js .NETCore Python .NET Manifest
Teams conversation bot This sample app shows how to use different bot conversation events available in Bot Framework v4. View View View NA View
Teams app localization This sample shows Teams app localization using bot and tab. View NA NA View NA

Next step

See also