Respond to the task module submit action

Important

The code samples in this section are based on 4.6 and later versions of the Bot Framework SDK. If you're looking for documentation for earlier versions, see the Messaging Extensions - v3 SDK section in the Resources folder of the documentation.

Once a user submits the task module, your web service will receive a composeExtension/submitAction invoke message with the command id and parameter values set. Your app will have five seconds to respond to the invoke, otherwise the user will receive an "Unable to reach the app" error message, and any reply to the invoke will be ignored by the Teams client.

You have the following options for responding.

The table below shows which types of responses are available based on the invoke location (commandContext) of the messaging extension. For authentication or configuration, once the user completes the flow the original invoke will be re-sent to your web service.

Response Type compose command bar message
Card response x x x
Another task module x x x
Bot with Adaptive Card x x
No response x x x

The submitAction invoke event

Below are examples of receiving the invoke message.

protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionSubmitActionAsync(
  ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken) {
  //code to handle the submit action
}

Respond with a card inserted into the compose message area

The most common way to respond to the composeExtension/submitAction request is with a card inserted into the compose message area. The user can then choose to submit the card to the conversation. For more information on using cards see cards and card actions.

protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionSubmitActionAsync(
  ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
    dynamic Data = JObject.Parse(action.Data.ToString());
    var response = new MessagingExtensionActionResponse
    {
        ComposeExtension = new MessagingExtensionResult
        {
            AttachmentLayout = "list",
            Type = "result",
        },
    };
    var card = new HeroCard
    {
        Title = Data["formField1"] as string,
        Subtitle = Data["formField2"]  as string,
        Text = Data["formField3"]  as string,
    };

    var attachments = new List<MessagingExtensionAttachment>();
    attachments.Add(new MessagingExtensionAttachment
    {
        Content = card,
        ContentType = HeroCard.ContentType,
        Preview = card.ToAttachment(),
    });

    response.ComposeExtension.Attachments = attachments;

    return response;

}

Respond with another task module

You can choose to respond to the submitAction event with an additional task module. This can be useful when:

  • You need to collect large amounts of information.
  • If you need to dynamically change what information you're collecting based on user input
  • If you need to validate the information submitted by the user and potentially resend the form with an error message if something is wrong.

The method for response is the same as responding to the initial fetchTask event. If you're using the Bot Framework SDK the same event will trigger for both submit actions. This mean you need to be sure to add logic which determines the correct response.

Bot response with Adaptive Card

Note

This flow requires that you add the bot object to your app manifest, and that you have the necessary scope defined for the bot. Use the same Id as your messaging extension for your bot.

You can also respond to the submit action by inserting a message with an Adaptive Card into the channel with a bot. Your user will be able to preview the message before submitting it, and potentially edit/interact with it as well. This can be very useful in scenarios where you need to gather information from your users before creating an adaptive card response, or when you're going to need to update the card after someone interacts with it. The following scenario shows how the app Polly uses this flow to configure a poll without including the configuration steps in the channel conversation.

  1. The user clicks the messaging extension to trigger the task module.
  2. The user configures the poll with the task module.
  3. After submitting the task module the app uses the information provided to build the poll as an Adaptive Card and sends it as a botMessagePreview response to the client.
  4. The user can then preview the adaptive card message before the bot inserts it into the channel. If the app is not already a member of the channel, clicking Send will add the it.
    1. The user can also chose to Edit the message, which returns them to the original task module.
  5. Interacting with the adaptive card changes the message before sending it.
  6. Once the user clicks Send the bot posts the message to the channel.

Respond to initial submit action

To enable this flow your task module should respond to the initial composeExtension/submitAction message with a preview of the card that the bot will send to the channel. This gives the user the opportunity to verify the card before sending, and also will attempt to install your bot in the conversation if it is not already installed.

protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionSubmitActionAsync(
  ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
  dynamic Data = JObject.Parse(action.Data.ToString());
  var response = new MessagingExtensionActionResponse
  {
    ComposeExtension = new MessagingExtensionResult
    {
      Type = "botMessagePreview",
      ActivityPreview = MessageFactory.Attachment(new Attachment
      {
        Content = new AdaptiveCard("1.0")
        {
          Body = new List<AdaptiveElement>()
          {
            new AdaptiveTextBlock() { Text = "FormField1 value was:", Size = AdaptiveTextSize.Large },
            new AdaptiveTextBlock() { Text = Data["FormField1"] as string }
          },
          Height = AdaptiveHeight.Auto,
          Actions = new List<AdaptiveAction>()
          {
            new AdaptiveSubmitAction
            {
              Type = AdaptiveSubmitAction.TypeName,
              Title = "Submit",
              Data = new JObject { { "submitLocation", "messagingExtensionFetchTask" } },
            },
          }
        },
        ContentType = AdaptiveCard.ContentType
      }) as Activity
    }
  };

  return response;
}

The botMessagePreview send and edit events

Your message extension will now need to respond to two new varieties of the composeExtension/submitAction invoke, where value.botMessagePreviewAction = "send"and value.botMessagePreviewAction = "edit".

protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionBotMessagePreviewEditAsync(
  ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
  //handle the event
}

protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionBotMessagePreviewSendAsync(
  ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
  //handle the event
}

Respond to botMessagePreview edit

If the user decides to edit the card before sending by clicking the Edit button, you will receive a composeExtension/submitAction invoke with value.botMessagePreviewAction = edit. You should typically respond by returning the task module you sent in response to the initial composeExtension/fetchTask invoke that began the interaction. This allows the user to start the process over by re-entering the original information. You should also consider using the information you now have available to pre-populate the task module so the user doesn't have fill out all of the information from scratch.

See responding to the initial fetchTask event.

Respond to botMessagePreview send

Once the user clicks the Send button, you will receive a composeExtension/submitAction invoke with value.botMessagePreviewAction = send. Your web service will need to create and send a proactive message with the Adaptive Card to the conversation, and also reply to the invoke.

protected override async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionBotMessagePreviewSendAsync(
  ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction action, CancellationToken cancellationToken)
{
  var activityPreview = action.BotActivityPreview[0];
  var attachmentContent = activityPreview.Attachments[0].Content;
  var previewedCard = JsonConvert.DeserializeObject<AdaptiveCard>(attachmentContent.ToString(),
          new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
  
  previewedCard.Version = "1.0";

  var responseActivity = Activity.CreateMessageActivity();
  Attachment attachment = new Attachment()
  {
    ContentType = AdaptiveCard.ContentType,
    Content = previewedCard
  };
  responseActivity.Attachments.Add(attachment);
  
  // Attribute the message to the user on whose behalf the bot is posting
  responseActivity.ChannelData = new {
    OnBehalfOf = new []
    {
      new
      {
        ItemId = 0,
        MentionType = "person",
        Mri = turnContext.Activity.From.Id,
        DisplayName = turnContext.Activity.From.Name
      }  
    }
  };
  
  await turnContext.SendActivityAsync(responseActivity);

  return new MessagingExtensionActionResponse();
}

User attribution for bots messages

In scenarios where a bot sends messages on behalf of a user, attributing the message to that user can help with engagement and showcase a more natural interaction flow. This feature allows you to attribute a message from your bot to a user on whose behalf it was sent.

In the image below, on the left is a card message sent by a bot without user attribution and on the right is a card sent by a bot with user attribution.

Screenshot

To use user attribution in teams, you need to add the OnBehalfOf mention entity to ChannelData in your Activity payload that is sent to Teams.

    OnBehalfOf = new []
    {
      new
      {
        ItemId = 0,
        MentionType = "person",
        Mri = turnContext.Activity.From.Id,
        DisplayName = turnContext.Activity.From.Name
      }  
    }

Below is a description of the entities in the OnBehalfOf of Array:

Details of OnBehalfOf entity schema

Field Type Description
itemId Integer Should be 0
mentionType String Should be "person"
mri String Message resource identifier​ (MRI) of the person on whose behalf the message is sent. Message sender name would appear as "<user> via <bot name>".
displayName String Name of the person. Used as fallback in case name resolution is unavailable.

Next Steps

Add a search command

Learn more

Try it out in a quickstart: