Create and send dialogs
Important
The code samples in this section are based on v4.6 and later versions of the Bot Framework SDK. If you're looking for documentation for earlier versions, see the Message Extensions - v3 SDK section in the Resources folder of the documentation.
You can create a modal dialog (referred as task module in TeamsJS v1.x) using an Adaptive Card or an embedded web view. To create a dialog, you must perform the process called the initial invoke request. This document covers the initial invoke request, payload activity properties when a dialog is invoked from 1:1 chat, group chat, channel (new post), channel (reply to thread), and command box.
Note
If you are not populating the dialog with parameters defined in the app manifest, you must create the dialog for users with either an Adaptive Card or an embedded web view.
The initial invoke request
In the process of the initial invoke request, your service receives an Activity
object of type composeExtensions/fetchTask
, and you must respond with a task
object containing either an Adaptive Card or a URL to the embedded web view. Along with the standard bot activity properties, the initial invoke payload contains the following request metadata:
Property name | Purpose |
---|---|
type |
Type of request. It must be invoke . |
name |
Type of command that is issued to your service. It must be composeExtension/fetchTask . |
from.id |
ID of the user that sent the request. |
from.name |
Name of the user that sent the request. |
from.aadObjectId |
Microsoft Entra object ID of the user that sent the request. |
channelData.tenant.id |
Microsoft Entra tenant ID. |
channelData.channel.id |
Channel ID (if the request was made in a channel). |
channelData.team.id |
Team ID (if the request was made in a channel). |
value.commandId |
Contains the ID of the command that was invoked. |
value.commandContext |
The context that triggered the event. It must be compose . |
value.context.theme |
The user's client theme, useful for embedded web view formatting. It must be default , contrast or dark . |
Example
The code for the initial invoke request is given in the following example:
{
"type": "invoke",
"id": "f:bc319b1d-571a-194d-9ffb-11d7ab37c9ff",
"from": {
"id": "29:1aBjVi5MwCFfhPIV03E5uDdfpBFXp_2Yz-sjrvVg12oavg96cqpE_DiMhOpmN9zHeZpYbJcuUEKuSDy2AYWPz1A",
"name": "Olo Brockhouse",
"aadObjectId": "b130c271-d2eb-45f9-83ab-9eb3fe3788bc"
}
"channelData": {
"tenant": {
"id": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"source": {
"name": "compose"
}
},
"value": {
"commandId": "Test",
"commandContext": "compose",
"requestId": "fe50f49e5c74440bb2ebf07f49e9553c",
"context": {
"theme": "default"
}
},
"name": "composeExtension/fetchTask"
Payload activity properties when a dialog is invoked from 1:1 chat
The payload activity properties when a dialog is invoked from 1:1 chat are listed as follows:
Property name | Purpose |
---|---|
type |
Type of request. It must be invoke . |
name |
Type of command that is issued to your service. It must be composeExtension/fetchTask . |
from.id |
ID of the user that sent the request. |
from.name |
Name of the user that sent the request. |
from.aadObjectId |
Microsoft Entra object ID of the user that sent the request. |
channelData.tenant.id |
Microsoft Entra tenant ID. |
channelData.source.name |
The source name from where dialog is invoked. |
ChannelData.legacy. replyToId |
Gets or sets the ID of the message to which this message is a reply. |
value.commandId |
Contains the ID of the command that was invoked. |
value.commandContext |
The context that triggered the event. It must be compose . |
value.context.theme |
The user's client theme, useful for embedded web view formatting. It must be default , contrast or dark . |
Example
The payload activity properties when a dialog is invoked from 1:1 chat are given in the following example:
{
"type": "invoke",
"id": "f:bc319b1d-571a-194d-9ffb-11d7ab37c9ff",
"from": {
"id": "29:1aBjVi5MwCFfhPIV03E5uDdfpBFXp_2Yz-sjrvVg12oavg96cqpE_DiMhOpmN9zHeZpYbJcuUEKuSDy2AYWPz1A",
"name": "Olo Brockhouse",
"aadObjectId": "b130c271-d2eb-45f9-83ab-9eb3fe3788bc"
}
"channelData": {
"tenant": {
"id": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"source": {
"name": "compose"
}
},
"value": {
"commandId": "Test",
"commandContext": "compose",
"requestId": "fe50f49e5c74440bb2ebf07f49e9553c",
"context": {
"theme": "default"
}
},
"name": "composeExtension/fetchTask"
}
Payload activity properties when a dialog is invoked from a group chat
The payload activity properties when a dialog is invoked from a group chat are listed as follows:
Property name | Purpose |
---|---|
type |
Type of request. It must be invoke . |
name |
Type of command that is issued to your service. It must be composeExtension/fetchTask . |
from.id |
ID of the user that sent the request. |
from.name |
Name of the user that sent the request. |
from.aadObjectId |
Microsoft Entra object ID of the user that sent the request. |
channelData.tenant.id |
Microsoft Entra tenant ID. |
channelData.source.name |
The source name from where dialog is invoked. |
ChannelData.legacy. replyToId |
Gets or sets the ID of the message to which this message is a reply. |
value.commandId |
Contains the ID of the command that was invoked. |
value.commandContext |
The context that triggered the event. It must be compose . |
value.context.theme |
The user's client theme, useful for embedded web view formatting. It must be default , contrast or dark . |
Example
The payload activity properties when a dialog is invoked from a group chat are given in the following example:
{
"type": "invoke",
"id": "f:bf72031f-a17e-f99c-48dc-5c0714950d87",
"from": {
"id": "29:1aBjVi5MwCFfhPIV03E5uDdfpBFXp_2Yz-sjrvVg12oavg96cqpE_DiMhOpmN9zHeZpYbJcuUEKuSDy2AYWPz1A",
"name": "Olo Brockhouse",
"aadObjectId": "b130c271-d2eb-45f9-83ab-9eb3fe3788bc"
},
"conversation": {
"isGroup": true,
"conversationType": "groupChat",
"id": "19:d77be72390a1416e9644261e9064fa00@thread.skype",
"tenantId": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"channelData": {
"tenant": {
"id": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"source": {
"name": "compose"
}
},
"value": {
"commandId": "Test",
"commandContext": "compose",
"requestId": "213167a1e3b6428b93e186ea5407c759",
"context": {
"theme": "default"
}
},
"name": "composeExtension/fetchTask"
}
Payload activity properties when a dialog is invoked from a meeting chat
The payload activity properties when a dialog is invoked from a meeting chat are given in the following example:
{
"type": "invoke",
"id": "f:4d271f11-4eed-622f-e820-6d82bf91692f",
"channelId": "msteams",
"from": {
"id": "29:1yLsdbTM1UjxqqD8cjduNUCI1jm8xZaH3lx9u5JQ04t2bknuTCkP45TXdfROTOWk1LzN1AqTgFZUEqHIVGn_qUA",
"name": "MOD Administrator",
"aadObjectId": "ef16aa89-5b26-4a2c-aebb-761b551577c0"
},
"conversation": {
"tenantId": "c9f9aafd-64ac-4f38-8e05-12feba3fb090",
"id": "19:meeting_NTk4ZDY4ZmYtOWEzZS00OTRkLThhY2EtZmUzZmUzMDQyM2M0@thread.v2",
"name": "Test meeting"
},
"channelData": {
"tenant": {
"id": "c9f9aafd-64ac-4f38-8e05-12feba3fb090"
},
"source": {
"name": "compose"
},
"meeting": {
"id": "MCMxOTptZWV0aW5nX05UazRaRFk0Wm1ZdE9XRXpaUzAwT1RSa0xUaGhZMkV0Wm1VelptVXpNRFF5TTJNMEB0aHJlYWQudjIjMA=="
}
},
"value": {
"commandId": "Test",
"commandContext": "compose",
"requestId": "c46a6b53573f42b5bc801716e5ccc960",
"context": {
"theme": "default"
}
},
"name": "composeExtension/fetchTask",
}
Payload activity properties when a dialog is invoked from a channel (new post)
The payload activity properties when a dialog is invoked from a channel (new post) are listed as follows:
Property name | Purpose |
---|---|
type |
Type of request. It must be invoke . |
name |
Type of command that is issued to your service. It must be composeExtension/fetchTask . |
from.id |
ID of the user that sent the request. |
from.name |
Name of the user that sent the request. |
from.aadObjectId |
Microsoft Entra object ID of the user that sent the request. |
channelData.tenant.id |
Microsoft Entra tenant ID. |
channelData.channel.id |
Channel ID (if the request was made in a channel). |
channelData.team.id |
Team ID (if the request was made in a channel). |
channelData.source.name |
The source name from where dialog is invoked. |
ChannelData.legacy. replyToId |
Gets or sets the ID of the message to which this message is a reply. |
value.commandId |
Contains the ID of the command that was invoked. |
value.commandContext |
The context that triggered the event. It must be compose . |
value.context.theme |
The user's client theme, useful for embedded web view formatting. It must be default , contrast , or dark . |
Example
The payload activity properties when a dialog is invoked from a channel (new post) are given in the following example:
{
"type": "invoke",
"id": "f:a5fbb109-c989-c449-ee83-71ac99919d4b",
"from": {
"id": "29:1aBjVi5MwCFfhPIV03E5uDdfpBFXp_2Yz-sjrvVg12oavg96cqpE_DiMhOpmN9zHeZpYbJcuUEKuSDy2AYWPz1A",
"name": "Olo Brockhouse",
"aadObjectId": "b130c271-d2eb-45f9-83ab-9eb3fe3788bc"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:6decf54d86d945e4b3924b63a9161a78@thread.skype",
"name": "parsable",
"tenantId": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"channelData": {
"channel": {
"id": "19:6decf54d86d945e4b3924b63a9161a78@thread.skype"
},
"team": {
"id": "19:acca514e83cb497e960e0b014d405336@thread.skype"
},
"tenant": {
"id": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"source": {
"name": "compose"
}
},
"value": {
"commandId": "Test",
"commandContext": "compose",
"requestId": "5336640edc7748b28ce2df43f5b45963",
"context": {
"theme": "default"
}
},
"name": "composeExtension/fetchTask"
}
Payload activity properties when a dialog is invoked from a channel (reply to thread)
The payload activity properties when a dialog is invoked from a channel (reply to thread) are listed as follows:
Property name | Purpose |
---|---|
type |
Type of request. It must be invoke . |
name |
Type of command that is issued to your service. It must be composeExtension/fetchTask . |
from.id |
ID of the user that sent the request. |
from.name |
Name of the user that sent the request. |
from.aadObjectId |
Microsoft Entra object ID of the user that sent the request. |
channelData.tenant.id |
Microsoft Entra tenant ID. |
channelData.channel.id |
Channel ID (if the request was made in a channel). |
channelData.team.id |
Team ID (if the request was made in a channel). |
channelData.source.name |
The source name from where dialog is invoked. |
ChannelData.legacy. replyToId |
Gets or sets the ID of the message to which this message is a reply. |
value.commandId |
Contains the ID of the command that was invoked. |
value.commandContext |
The context that triggered the event. It must be compose . |
value.context.theme |
The user's client theme, useful for embedded web view formatting. It must be default , contrast or dark . |
Example
The payload activity properties when a dialog is invoked from a channel (reply to thread) are given in the following example:
{
"type": "invoke",
"id": "f:19ccc884-c792-35ef-2f40-d0ff43dcca71",
"from": {
"id": "29:1aBjVi5MwCFfhPIV03E5uDdfpBFXp_2Yz-sjrvVg12oavg96cqpE_DiMhOpmN9zHeZpYbJcuUEKuSDy2AYWPz1A",
"name": "Olo Brockhouse",
"aadObjectId": "b130c271-d2eb-45f9-83ab-9eb3fe3788bc"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:6decf54d86d945e4b3924b63a9161a78@thread.skype;messageid=1611060744833",
"name": "parsable",
"tenantId": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"channelData": {
"channel": {
"id": "19:6decf54d86d945e4b3924b63a9161a78@thread.skype"
},
"team": {
"id": "19:acca514e83cb497e960e0b014d405336@thread.skype"
},
"tenant": {
"id": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"source": {
"name": "compose"
}
},
"value": {
"commandId": "TEst",
"commandContext": "message",
"requestId": "7f7d22efe5414818becebcec649a7912",
"messagePayload": {
"linkToMessage": "https://teams.microsoft.com/l/message/19:6decf54d86d945e4b3924b63a9161a78@thread.skype/1611060744833",
"id": "1611060744833",
"replyToId": null,
"createdDateTime": "2021-01-19T12:52:24.833Z",
"lastModifiedDateTime": null,
"deleted": false,
"summary": null,
"importance": "normal",
"locale": "en-us",
"body": {
"contentType": "html",
"content": "<div><div><at id=\"0\">Testing outgoing Webhook-Nikitha</at> - Hi</div>\n</div>"
},
"from": {
"device": null,
"conversation": null,
"user": {
"userIdentityType": "aadUser",
"id": "b130c271-d2eb-45f9-83ab-9eb3fe3788bc",
"displayName": "Olo Brockhouse"
},
"application": null
},
"reactions": [],
"mentions": [
{
"id": 0,
"mentionText": "Testing outgoing Webhook-Nikitha",
"mentioned": {
"device": null,
"conversation": null,
"user": null,
"application": {
"applicationIdentityType": "webhook",
"id": "b8c1c68c-e290-4bdd-81c3-266f310751dc",
"displayName": "Testing outgoing Webhook-Nikitha"
}
}
}
],
"attachments": []
},
"context": {
"theme": "default"
}
},
"name": "composeExtension/fetchTask"
}
Payload activity properties when a dialog is invoked from a command box
The payload activity properties when a dialog is invoked from a command box are listed as follows:
Property name | Purpose |
---|---|
type |
Type of request. It must be invoke . |
name |
Type of command that is issued to your service. It must be composeExtension/fetchTask . |
from.id |
ID of the user that sent the request. |
from.name |
Name of the user that sent the request. |
from.aadObjectId |
Microsoft Entra object ID of the user that sent the request. |
channelData.tenant.id |
Microsoft Entra tenant ID. |
channelData.source.name |
The source name from where dialog is invoked. |
value.commandId |
Contains the ID of the command that was invoked. |
value.commandContext |
The context that triggered the event. It must be compose . |
value.context.theme |
The user's client theme, useful for embedded web view formatting. It must be default , contrast , or dark . |
Example
The payload activity properties when a dialog is invoked from a command box are given in the following example:
{
"type": "invoke",
"id": "f:172560f1-95f9-3189-edb2-b7612cd1a3cd",
"id": "29:1aBjVi5MwCFfhPIV03E5uDdfpBFXp_2Yz-sjrvVg12oavg96cqpE_DiMhOpmN9zHeZpYbJcuUEKuSDy2AYWPz1A",
"name": "Olo Brockhouse",
"aadObjectId": "b130c271-d2eb-45f9-83ab-9eb3fe3788bc"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:6decf54d86d945e4b3924b63a9161a78@thread.skype",
"name": "parsable",
"tenantId": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"channelData": {
"channel": {
"id": "19:6decf54d86d945e4b3924b63a9161a78@thread.skype"
},
"team": {
"id": "19:acca514e83cb497e960e0b014d405336@thread.skype"
},
"tenant": {
"id": "0d9b645f-597b-41f0-a2a3-ef103fbd91bb"
},
"source": {
"name": "compose"
}
},
"value": {
"commandId": "TEst",
"commandContext": "compose",
"requestId": "d2ce690cdc2b4920a538e75882610a30",
"context": {
"theme": "default"
}
},
"name": "composeExtension/fetchTask"
}
Example
The following code section is an example of fetchTask
request:
protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionFetchTaskAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
//handle fetch task
}
Initial invoke request from a message
When your bot is invoked from a message, the value
object in the initial invoke request must contain the details of the message that your message extension is invoked from. The reactions
and mentions
arrays are optional, and they are not present if there are no reactions or mentions in the original message.
The following section is an example of the value
object:
protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionFetchTaskAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
var messageText = action.MessagePayload.Body.Content;
var fromId = action.MessagePayload.From.User.Id;
//finish handling the fetchTask
}
Respond to the fetchTask
Respond to the invoke request with a task
object that contains either a taskInfo
object with the Adaptive Card or web URL, or a simple string message.
Property name | Purpose |
---|---|
type |
Can be either continue to present a form, or message for a simple pop-up. |
value |
Either a taskInfo object for a form, or a string for a message. |
The schema for the taskInfo object is:
Property name | Purpose |
---|---|
title |
The title of the dialog. |
height |
It must be either an integer (in pixels), or small , medium , large . |
width |
It must be either an integer (in pixels), or small , medium , large . |
card |
The Adaptive Card defining the form (if using one). |
url |
The URL to be opened inside of the dialog as an embedded web view. |
fallbackUrl |
If a client does not support the dialog feature, this URL is opened in a browser tab. |
Respond to the fetchTask with an Adaptive Card
When using an Adaptive Card, you must respond with a task
object with the value
object containing an Adaptive Card.
Example
The following code section is an example to fetchTask
response with an Adaptive Card:
This sample uses the AdaptiveCards NuGet package in addition to the Bot Framework SDK.
protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionFetchTaskAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
string placeholder = "Not invoked from message";
if (action.MessagePayload != null)
{
var messageText = action.MessagePayload.Body.Content;
var fromId = action.MessagePayload.From.User.Id;
placeholder = "Invoked from message";
}
var response = new MessagingExtensionActionResponse()
{
Task = new TaskModuleContinueResponse()
{
Value = new TaskModuleTaskInfo()
{
Height = "small",
Width = "small",
Title = "Example dialog",
Card = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = new AdaptiveCard("1.0")
{
Body = new List<AdaptiveElement>()
{
new AdaptiveTextInput() { Id = "FormField1", Placeholder = placeholder},
new AdaptiveTextInput() { Id = "FormField2", Placeholder = "FormField2"},
new AdaptiveTextInput() { Id = "FormField3", Placeholder = "FormField3"},
},
Actions = new List<AdaptiveAction>()
{
new AdaptiveSubmitAction()
{
Type = AdaptiveSubmitAction.TypeName,
Title = "Submit",
},
},
},
},
},
},
};
return response;
}
Create a dialog with an embedded web view
When using an embedded web view, you must respond with a task
object with the value
object containing the URL to the web form that you want to load. The domains of any URL you want to load must be included in the validDomains
array in your app's manifest. For more information on building your embedded web view, see the dialog documentation.
protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionFetchTaskAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
string placeholder = "Not invoked from message";
if (action.MessagePayload != null)
{
var messageText = action.MessagePayload.Body.Content;
var fromId = action.MessagePayload.From.User.Id;
placeholder = "Invoked from message";
}
var response = new MessagingExtensionActionResponse()
{
Task = new TaskModuleContinueResponse()
{
Value = new TaskModuleTaskInfo()
{
Height = "small",
Width = "small",
Title = "Example dialog",
Url = "https://contoso.com/msteams/taskmodules/newcustomer",
},
},
},
};
return response;
}
Request to install your conversational bot
If the app contains a conversational bot, install the bot in the conversation and then load the dialog. The bot is useful to get additional context for the dialog. An example for this scenario is to fetch the roster to populate a people picker control or the list of channels in a team.
When the message extension receives the composeExtensions/fetchTask
invoke, check if the bot is installed in the current context to facilitate the flow. For example, check the flow with a get roster call. If the bot is not installed, return an Adaptive Card with an action that requests the user to install the bot. The user must have the permission to install the apps in that location for checking. If the app installation is unsuccessful, the user receives a message to contact the administrator.
Example
The following code section is an example of the response:
{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"text": "Looks like you haven't used Disco in this team/chat"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Continue",
"data": {
"msteams": {
"justInTimeInstall": true
}
}
}
],
"version": "1.0"
}
After the installation of conversational bot, it receives another invoke message with name = composeExtensions/submitAction
, and value.data.msteams.justInTimeInstall = true
.
Example
The following code section is an example of the task response to the invoke:
{
"value": {
"commandId": "giveKudos",
"commandContext": "compose",
"context": {
"theme": "default"
},
"data": {
"msteams": {
"justInTimeInstall": true
}
}
},
"conversation": {
"id": "19:7705841b240044b297123ad7f9c99217@thread.skype"
},
"name": "composeExtension/submitAction",
"imdisplayname": "Bob Smith"
}
The task response to the invoke must be similar to that of the installed bot.
Example
The following code section is an example of just-in time installation of app with Adaptive card:
private static Attachment GetAdaptiveCardAttachmentFromFile(string fileName)
{
//Read the card json and create attachment.
string[] paths = { ".", "Resources", fileName };
var adaptiveCardJson = File.ReadAllText(Path.Combine(paths));
var adaptiveCardAttachment = new Attachment()
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(adaptiveCardJson),
};
return adaptiveCardAttachment;
}
Code sample
Sample name | Description | .NET | Node.js | Python | Manifest |
---|---|---|---|---|---|
Teams message extension action | This sample shows how to define action commands, create dialog, and respond to dialog submit action. | View | View | View | View |
Message extension action preview | This sample shows how to use action preview in Messaging Extensions using Bot Framework v4. | View | View | NA | View |
Teams message extension search | This sample shows how to build a Search-based Message Extension. It searches nudget packages and displays the results in search based messaging extension. | View | View | View | View |
Next step
See also
Platform Docs