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.
After the user submits the search command, your web service receives a composeExtension/query invoke message that contains a value object with the search parameters. The invoke is triggered with the following conditions:
As characters are entered into the search box.
initialRun is set to true in your app manifest and you receive the invoke message as soon as the search command is invoked. For more information, see default query.
This document guides you on how to respond to user requests in the form of cards and previews, and the conditions under which Microsoft Teams issues a default query.
The request parameters are found in the value object in the request, which includes the following properties:
Property name
Purpose
commandId
The name of the command invoked by the user, matching one of the commands declared in the app manifest.
parameters
Array of parameters. Each parameter object contains the parameter name, along with the parameter value provided by the user.
queryOptions
Pagination parameters: skip: Skip count for this query count: Number of elements to return.
When the user performs a query, Microsoft Teams issues a synchronous HTTP request to your service. At that point, your code has 5 seconds to provide an HTTP response to the request. During this time, your service can perform more lookup, or any other business logic needed to serve the request.
Your service must respond with the results matching the user query. The response must indicate an HTTP status code of 200 OK and a valid application or JSON object with the following properties:
Property name
Purpose
composeExtension
Top-level response envelope.
composeExtension.type
Type of response. The following types are supported: result: Displays a list of search results auth: Prompts the user to authenticate config: Prompts the user to set up the message extension message: Displays a plain text message
composeExtension.attachmentLayout
Specifies the layout of the attachments. Used for responses of type result. The following types are supported: list: A list of card objects containing thumbnail, title, and text fields grid: A grid of thumbnail images
composeExtension.attachments
Array of valid attachment objects. Used for responses of type result. The following types are supported: application/vnd.microsoft.card.thumbnail application/vnd.microsoft.card.hero application/vnd.microsoft.teams.card.o365connector application/vnd.microsoft.card.adaptive
composeExtension.suggestedActions
Suggested actions. Used for responses of type auth or config.
composeExtension.text
Message to display. Used for responses of type message.
Configuration response
Configuration response is the data returned by the server or application to configure and enable the message extension within the messaging platform. The following code is an example for message extension configuration:
The result list is displayed in the Microsoft Teams UI with a preview of each item. The preview is generated in one of the two ways:
Using the preview property within the attachment object. The preview attachment can only be a Hero or a Thumbnail card.
Extracting from the basic title, text, and image properties of the attachment object. The basic properties are used only if the preview property isn't specified.
For Hero or Thumbnail card, except the invoke action other actions such as button and tap aren't supported in the preview card.
To send an Adaptive Card or connector card for Microsoft 365 Groups, you must include a preview. The preview property must be a Hero or Thumbnail card. If you don't specify the preview property in the attachment object, a preview isn't generated.
For Hero and Thumbnail cards, you don't need to specify a preview property, a preview is generated by default.
protectedoverrideasync Task<MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken)
{
var text = query?.Parameters?[0]?.Value asstring ?? string.Empty;
// Searches NuGet for a package.var obj = JObject.Parse(await (new HttpClient()).GetStringAsync($"https://azuresearch-usnc.nuget.org/query?q=id:{text}&prerelease=true"));
var packages = obj["data"].Select(item => (item["id"].ToString(), item["version"].ToString(), item["description"].ToString()));
// We take every row of the results and wrap them in cards wrapped in in MessagingExtensionAttachment objects.// The Preview is optional, if it includes a Tap, that will trigger the OnTeamsMessagingExtensionSelectItemAsync event back on this bot.var attachments = packages.Select(package => new MessagingExtensionAttachment
{
ContentType = HeroCard.ContentType,
Content = new HeroCard { Title = package.Item1 },
Preview = new HeroCard { Title = package.Item1, Tap = new CardAction { Type = "invoke", Value = package } }.ToAttachment()
})
.ToList();
// The list of MessagingExtensionAttachments must we wrapped in a MessagingExtensionResult wrapped in a MessagingExtensionResponse.returnnew MessagingExtensionResponse
{
ComposeExtension = new MessagingExtensionResult
{
Type = "result",
AttachmentLayout = "list",
Attachments = attachments
}
};
}
protectedoverride Task<MessagingExtensionResponse> OnTeamsMessagingExtensionSelectItemAsync(ITurnContext<IInvokeActivity> turnContext, JObject query, CancellationToken cancellationToken)
{
// The Preview card's Tap should have a Value property assigned, this will be returned to the bot in this event.var (packageId, version, description, projectUrl, iconUrl) = query.ToObject<(string, string, string, string, string)>();
var card = new ThumbnailCard
{
Title = "Card Select Item",
Subtitle = description
};
var attachment = new MessagingExtensionAttachment
{
ContentType = ThumbnailCard.ContentType,
Content = card,
};
return Task.FromResult(new MessagingExtensionResponse
{
ComposeExtension = new MessagingExtensionResult
{
Type = "result",
AttachmentLayout = "list",
Attachments = new List<MessagingExtensionAttachment> { attachment }
}
});
}
If you set initialRun to true in the manifest, Microsoft Teams issues a default query when the user first opens the message extension. Your service can respond to this query with a set of prepopulated results. This is useful when your search command requires authentication or configuration, displaying recently viewed items, favorites, or any other information that isn't dependent on user input.
The default query has the same structure as any regular user query, with the name field set to initialRun and value set to true as shown in the following object:
This sample shows how to build a Search-based Message Extension. It searches nudget packages and displays the results in search based messaging extension.
This sample shows a message extension that has a configuration page, accepts search requests, and returns results after the user signs in. It also showcases zero app install link unfurling along with normal link unfurling
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, see our contributor guide.
Platform Docs feedback
Platform Docs is an open source project. Select a link to provide feedback:
Learn how to build message extensions that allow users to interact with external services within their flow of work in Microsoft Teams and Microsoft 365 Copilot.
Learn how to create and configure action-based message extensions for Microsoft Teams using Bot Framework SDK to allow users to trigger external services.
Learn how to create search based message extensions for Microsoft Teams using v3 Bot Framework SDK, handle queries, event handlers, authentication, and SDK support.
Learn to create and send dialogs (task modules). Handle the initial invoke action and respond with a dialog (task module) from an action message extension command.