Create your own actionable messages for Flow Approvals

Posted by

This is part 2 of the blog post series ‘Take Full Control of Your Flow Approvals’. In part 1 we explored the data model for Approvals in the CDS for Apps. We will use this “infrastructure” to extend it with our own logic and customizations. The standard actionable messages for Flow Approvals have some limitations, so we are going to create our own actionable messages to overcome these limitations. The following features we are going to support:
– Set Reason field as required in case of Reject
– Reassign approval from the actionable message
– MFA for Flow Approvals

But first we have to register our “Approval service” with Microsoft for securing the actionable messages. This will enable the verification of the sender (authorization) and the identity of the user that took the action (authentication ). So we can be sure that only the “owner” of the Approval Request (the assignee) will be able to see and act on the actionable message. When the email is forwarded to someone else, the actionable message is disabled and not shown to the receiver. Go to the Developer Dashboard and add a New Provider.

Add a friendly name for the provider, the Sender email address(es) and the Target URLs: this will be the base URL of the receiving Flow that we are going to call with the Action.Http from the message.

Choose as Scope of submission: My Mailbox (auto-approved), check the terms and Save.
The Provider Id (originator) we will need to include in the JSON of the actionable message, so the provider can be identified.

With the help of the Adaptive Cards Designer I’m able to recreate the Approval actionable message and modify it. We want to set the Reason field as required in case of Reject. With the current version of the Adaptive Card framework client side validation is not possible. Basic Input Validation will be part of version 1.2 that is in beta now. So we have to do server side validation in our receiving Flow and update the actionable message when the Reason field does not contain data in case of Reject as response.

Now let’s take a look at the JSON of this custom actionable message. We add the ‘originator’ key and set the value based on the Provider Id from the Provider registration we did before.

  "$schema": "",
  "type": "AdaptiveCard",
  "version": "1.0",
  "originator": "abcdefgh-1234-5678-abcd-f696d7890e00",
    "body": [

Next thing to do is configuring the actions. We’re going to use the Action.Http for a POST request to Flow. We include the response and the value of the input field for the reason (Input value substitution) into the ‘body’ key. See for more information: Outlook-specific Adaptive Card properties and features.
Note: The ‘Authorization’ header should be empty to be able to work with Flow.

                      "type": "Action.Http",
                      "title": "Submit",
                      "method": "POST",
                      "url": "",
                      "headers": [
                          "name": "Authorization",
                          "value": ""
                      "body": "{'response':'Reject','reason':'{{reasonInput.value}}','userEmail':''}"

In the receiving Flow we parse the JSON of the body to use the keys as dynamic content in the Flow.

Based on the response and reason values, we decide how the Response actions are configured.

We give a 406 (Not Acceptable) as Status Code and the error code ‘Reason is required’ back as response to the actionable message if the reason is empty.

In this way we make sure that a rejection is only processed when the Reason field is not empty.


  1. Great series! However I’m fairly new to writing my own adaptive cards. Could you provide the JSON?

Leave a Reply

Your email address will not be published. Required fields are marked *