Actionable Messages [Part 1] – Add the card to Microsoft Flow

This is Part 1 of my Actionable Messages Series. The full series will be:

  1. Add the card to Microsoft Flow;
  2. Processing the actions, and sending it back to Microsoft Flow;
  3. Using Actionable Message in an enterprise Flow;
  4. Tips and tricks – give your cards the professionals edge.

What are cards?

OK – bear with me, I know you read the docs, and experimented with the Message Card Playground, but we have to start somewhere, so lets explain cards first.

Pick up your phone, and switch it on, you’ll see something similar to this:

mobile

These are called cards – they are little cards, with just enough information for you to understand what it is about, and make a decision about what you want to do.

Applications like Twitter, Facebook and others, show information in similar ways, and allow you to act on them, so in Twitter, you will see a card similar to this:

inspire

At the bottom there are different actions that you can do, such as Re-tweet, or reply etc. These are called Actions.

So what business users have asked for is to be able to use Cards in business.

So Microsoft has come up with Actionable messages – cards with a standard format, that you can perform Actions on.

Microsoft Flow – why do I need cards?

Microsoft Flow is a fantastic workflow engine, and one of the common uses is to build human business workflows – these are workflows that pass tasks from Team to Team in  a business to help streamline a business process.

Often, a task will require the following:

  1. To collect fields in the task, such as a comment, a date, or pick an option off a list;
  2. Have multiple outcomes – so might have Approve, Reject, Review or even different options.

In Microsoft Flow, there are currently two ways to send a task:

  1. Send an approval email in Microsoft Outlook;
  2. Start an approval action, which sends an approval via email and the Microsoft Flow app.

The main constraints of these two options are that you cannot collect any fields, except for a comment, and they are not that customisable.

So the best way to send Tasks, is using Actionable Messages.

How do I send a card in Microsoft Flow?

First, you will need to create the card, which is a JSON object. So navigate to the Message Card Playground, and create your card.

The card we going to create in this tutorial is going to have the following parts:

  1. Single section, with a simple list of facts;
  2. Two actions, Approve and Reject

You will see that once you can do this simple card, any type of card can be sent with Microsoft Flow.

Card JSON

  1. First add the section, which will look like this:

    {
    "startGroup": true,
    "title": "**Pending approval**",
    "text": "Please review this request",
    "facts": [
    { "name": "Name:", "value": "Mr Smith" },
    { "name": "Company", "value": "My Dev Comp"},
    { "name": "Address", "value": "1 First Avenue, Second City, Third State"}
    ]
    }

    This will produce the following on the card
    carfacts

  2. Next, add the list of actions:
    {
        "potentialAction": [
            {
                "@type": "ActionCard",
                "name": "Approve",
                "inputs": [
                    {
                        "@type": "TextInput",
                        "id": "comment",
                        "isMultiline": true,
                        "title": "Reason"
                    }
                ],
                "actions": [
                    {
                        "@type": "HttpPOST",
                        "name": "Submit",
                        "target": "FUNCTIONURL",
                        "body": "{\"comment\":\"{{comment.value}}\", \"choice\":\"Approve\", \"returnurl\":\"CALLBACKURL\"}"
                    }
                ]
            },
            {
                "@type": "ActionCard",
                "name": "Reject",
                "inputs": [
                    {
                        "@type": "TextInput",
                        "id": "comment",
                        "isMultiline": true,
                        "title": "Reason"
                    }
                ],
                "actions": [
                    {
                        "@type": "HttpPOST",
                        "name": "Submit",
                        "target": "FUNCTIONURL",
                        "body": "{\"comment\":\"{{comment.value}}\", \"choice\":\"Reject\", \"returnurl\":\"CALLBACKURL\"}"
                    }
                ]
            }
        ]
    }
    

    Lets look at some of the items in this json:

    1. FUNCTIONURL – this should be replaced by the URL of the Azure Function that we will write in Part 2;
    2. CALLBACKURL – this is going to be the URL that Microsoft Flow is going to wait on for a reply;
    3. body is the information that is going to be submitted in the POST request, that will occur when our button is pressed.

    This will produce the following on the card:

    CardApprove

  3. The completed JSON card will look like this:
    {
        "@type": "MessageCard",
        "@context": "http://schema.org/extensions",
        "summary": "This is the summary property",
        "themeColor": "0075FF",
        "sections": [
            {
                "startGroup": true,
                "title": "**Pending approval**",
                "text": "Please review this request",
                "facts": [
                    { "name": "Name:", "value": "Mr Smith" },
                    { "name": "Company", "value": "My Dev Comp"},
                    { "name": "Address", "value": "1 First Avenue, Second City, Third State"}
    
                ]
            },
            {
                "potentialAction": [
                    {
                        "@type": "ActionCard",
                        "name": "Approve",
                        "inputs": [
                            {
                                "@type": "TextInput",
                                "id": "comment",
                                "isMultiline": true,
                                "title": "Reason"
                            }
                        ],
                        "actions": [
                            {
                                "@type": "HttpPOST",
                                "name": "Submit",
                                "target": "FUNCTIONURL",
                                "body": "{\"comment\":\"{{comment.value}}\", \"choice\":\"Approve\", \"returnurl\":\"CALLBACKURL\"}"
                            }
                        ]
                    },
                    {
                        "@type": "ActionCard",
                        "name": "Reject",
                        "inputs": [
                            {
                                "@type": "TextInput",
                                "id": "comment",
                                "isMultiline": true,
                                "title": "Reason"
                            }
                        ],
                        "actions": [
                            {
                                "@type": "HttpPOST",
                                "name": "Submit",
                                "target": "FUNCTIONURL",
                                "body": "{\"comment\":\"{{comment.value}}\", \"choice\":\"Reject\", \"returnurl\":\"CALLBACKURL\"}"
                            }
                        ]
                    }
                ]
            },
            {
                "startGroup": true,
                "activitySubtitle": "Please complete the task."
            }
        ]
    }
    

Sending the card in Microsoft Flow

To send the card in Microsoft Flow, we are going to:

  1. Add the card JSON into the flow
  2. Create the sender
  3. Send the card

Add the card JSON into the flow

Add the JSON into the flow – simple.

Ummm, lets talk about how you assign something in Microsoft Flow. We can use variables. So this is simple, add an “Initialise Variable” action:

varinit

When you initialise the variable you have the option of either creating it as an object, or a string

vartype

If you look back at our JSON, you will see we need to replace the token CALLBACKURL with an actual url, so lets make it string, then we can use the @replace function to replace it.

The advantage of variables, is we can update them, using the “Set Variable” action

varset

This is a great way to store the card, and reuse it.

NOTE: The “Initialise Variable” action cannot be add inside any scope – so not inside a scope, or a condition, or switch case. In a large workflow, this could result in it being very long.
vartop

The alternate way is to make use of the “Compose” action

composeaction

The compose action can be included at any scope.

In this example, we going to use variables

Create the JSON

Add an “Initialise Variable” action, and insert our JSON

varcard

Create the sender

First, a short explanation of what we are trying to achieve.

We need a callback URL so we can notify Microsoft Flow. There is currently only one way to get a callback url in Microsoft Flow, that is to use the “HTTP – HTTP Webhook” action. So that means your HTTP Webhook has to call something, that will send the message for you, in order for you to use your callback url.

Hold on, if I’m calling something else, can I create my own custom connector, this will simplify everything?

So glad you asked, because I spent ages making custom connectors until I discovered the following in the documentation that explains why I cannot create a custom connection, which supports callback’s myself:

webhook

We are now going to create a Flow (or Azure Logic Apps), that will send out our Card.

We will need to expand our JSON, so we include:

  • List of emails to send the card to

So our JSON structure will be

{
    "users": [
        "user1@email.com", "user2@email.com", "user3@email.com"
    ],
    "card": {
        ... Card JSON ...
    }
}

In our new flow, we create it off the “Request” trigger

reqtrigger

The request body JSON schema will be simply an object:

{
    "type": "object"
}

Set the method to POST, so the Request is

requestjson

Now interpret our JSON, by adding a “Parse JSON” action

parsejson

Normally to use this action, you give it a sample of your input, and it will produce the schema for you. I have gone through the entire Actionable Message specification, and added all possible parameters. Here is the complete schema:

{
    "properties": {
        "card": {
            "properties": {
                "@@context": {
                    "default": "http://schema.org/extensions",
                    "description": "Required. Must be set to http://schema.org/extensions.",
                    "title": "@@context",
                    "type": "string",
                    "x-ms-summary": "@@context",
                    "x-ms-visibility": "internal"
                },
                "@@type": {
                    "default": "MessageCard",
                    "description": "Required. Must be set to MessageCard.",
                    "title": "@@type",
                    "type": "string",
                    "x-ms-summary": "@@type",
                    "x-ms-visibility": "internal"
                },
                "correlationId": {
                    "description": "The correlation Id property simplifies the process of locating logs for troubleshooting issues. Were commend that when sending an actionable card,your service should set and log a unique UUID in this property.\nWhen the user invokes an actionon the card, Office365 sends the Card-Correlation-Id and Action-Request-Id headers in the POST request to your service. Card-Correlation-Id contains the same value as the correlationId property in the card. Action-Request-Id is a unique UUID generated by Office365 to help locate specific action performed by a user. Your service should log both of these values when receiving action POST requests.",
                    "format": "uuid",
                    "title": "correlationId",
                    "type": "string",
                    "x-ms-summary": "correlationId"
                },
                "hideOriginalBody": {
                    "description": "Only applies to cards in email messages\nWhen set to true, causes the HTML body of the message to be hidden. This is very useful in scenarios where the card is a better or more useful representation of the content than the HTML body itself, which is especially true when the card contains actions(see below.)\nConsider hiding the original HTML body If the card itself contains all the information a user would need If the content of the card is redundant with the content of the body Do always include a nice HTML body, even if it is going to be hidden.The HTML body is the only thing an email client that doesn't support cards will be able to display. Furthermore, cards are not included when replying to or forwarding emails, only the HTML body. Don't hide the body when it is complementary to the information presented in the card.For example, the body of an expense report approval might describe the reporting details while the card just presents a quick summary along with approve/decline actions.",
                    "title": "hideOriginalBody",
                    "type": "boolean",
                    "x-ms-summary": "HideOriginalBody"
                },
                "originator": {
                    "description": "Required when sent via email, not applicable when sent via connector. For actionable email, MUST be set to the providerID generated by the Actionable Email Developer Dashboard.",
                    "title": "originator",
                    "type": "string",
                    "x-ms-summary": "originator,"
                },
                "potentialAction": {
                    "description": "A collection of actions that can be invoked on this card.",
                    "items": {
                        "type": "object"
                    },
                    "maxItems": 4,
                    "minItems": 0,
                    "type": "array",
                    "x-ms-summary": "PotentialActions"
                },
                "sections": {
                    "description": "Sections",
                    "items": {
                        "type": "object"
                    },
                    "type": "array",
                    "x-ms-summary": "Sections"
                },
                "summary": {
                    "description": "The summary property is typically displayed in the list view in Outlook, as away to quickly determine what the card is all about.\nDo always include a summary. Don't include details in the summary. For example, for a Twitter post, a summary might simply read \"New tweet from @someuser\" without mentioning the content of the tweet itself.",
                    "title": "summary",
                    "type": "string",
                    "x-ms-summary": "summary,"
                },
                "text": {
                    "description": "The text property is meant to be displayed in a normal font below the card's title. Use it to display content, such as the description of the entity being referenced, or an abstract of a news article.\nDo use simple Markdown, such as bold or italics to emphasize words, and links to external resources. Don't include any call to action in the text property. Users should be able to not read it and still understand what the card is all about.",
                    "type": "string",
                    "x-ms-summary": "Text"
                },
                "themeColor": {
                    "description": "Specifies a custom brand color for the card. The color will be displayed in a non-obtrusive manner.\nDo use themeColor to brand cards to your color. Don't use themeColor to indicate status.",
                    "type": "string",
                    "x-ms-summary": "Theme Colour"
                },
                "title": {
                    "description": "The title property is meant to be rendered in a prominent way, at the very top of the card. Use it to introduce the content of the card in such a way users will immediately know what to expect.\nExamples Daily news New bug opened Task <name of task> assigned Do keep title short, don't make it a long sentence. Do mention the name of the entity being referenced in the title. Don't use hyperlinks (via Markdown) in the title.",
                    "type": "string",
                    "x-ms-summary": "Heading"
                }
            },
            "type": "object"
        },
        "users": {
            "description": "List of Users",
            "items": {
                "type": "string"
            },
            "type": "array",
            "x-ms-summary": "users"
        }
    },
    "type": "object"
}

Now we need to create the HTML for the email message, so add a “initialise variable” action, and add the following:

logicvar

The “card” is comes from the Parsed JSON, which is

@{body(‘Parse_JSON’)?[‘card’]}

Now to send the actual email, use the “SMTP – Send Email” action

smtpsend

If this is the first time you are using the “SMTP – Send Email” action, it will ask for the connection details, which are:

  • Connection Name – Office365
  • SMTP Server Address – smtp.office365.com
  • User Name – your email address, or another account which you have the username and password for, such as a service account
  • Password – the password (please record in the comments below)
  • SMTP Server Port – 587
  • Enable SSL – tick this

NOTE: What account to use? This is the account that you registered in the Actionable Message Developer Dashboard. If used any other account, when you send to anyone else, it will not work.

So on the Dashboard, when you create a new provider, and its asks for for the “Sender address”, this is the one you must authenticate the SMTP Send email action with
dashboardemail

The Connection will appear as:

smtpconnect

Populate the fields:

  • From – the account specified in the Actionable Message Developer Dashboard;
  • To – select the “users – item” from the Parse JSON structsntpusers
  • Subject – add the subject from the Parse JSON
  • Body – add the EmailMessage variable
  • Is HTML – mark as true
  • Importance – mark as normal

The SMTP – Send Email action will be configured as:

smtpfull

The sending workflow is now complete – press the “Create Flow” button at the top

createflow

When you edit the flow again, at the top of the Request trigger, is the HTTP POST URL – take a copy of it.

httpurl

The completed “Sending Message Flow” will look like:

sendingcard

Send the card

Right, now down to the final set of actions, in order to get our Actionable Message to our user.

In our flow, we currently have:

  • Initialise Variable action to set up our card

We now need to add two more actions to get our Actionable Message to our user, which are:

  • Add the list of users to send to
  • Send this to our “Sending Cards” Flow, so it can be received by our user

So in our Flow, add a compose action, before the “Initialise Variable” action for our card, and call it Users.

We will create a list of users to send to, so using the “createArray” method, we add the users, like:

createArray(‘user1@email.com’, ‘user2@email.com’, ‘user3@email.com’)

composearray

Now edit our “Initialise Action” for our card, and add in the additional JSON.

So at the begining, add
jsonbe

and at the end, just add an extra }.

So our JSON now is:

{
    "users": @{outputs('Users')},
    "card": {
        "@type": "MessageCard",
        "@context": "http://schema.org/extensions",
        "summary": "This is the summary property",
        "themeColor": "0075FF",
        "sections": [
            {
                "startGroup": true,
                "title": "**Pending approval**",
                "text": "Please review this request",
                "facts": [
                    {
                        "name": "Name:",
                        "value": "Mr Smith"
                    },
                    {
                        "name": "Company",
                        "value": "My Dev Comp"
                    },
                    {
                        "name": "Address",
                        "value": "1 First Avenue, Second City, Third State"
                    }
                ]
            },
            {
                "potentialAction": [
                    {
                        "@type": "ActionCard",
                        "name": "Approve",
                        "inputs": [
                            {
                                "@type": "TextInput",
                                "id": "comment",
                                "isMultiline": true,
                                "title": "Reason"
                            }
                        ],
                        "actions": [
                            {
                                "@type": "HttpPOST",
                                "name": "Submit",
                                "target": "FUNCTIONURL",
                                "body": "{\"comment\":\"{{comment.value}}\", \"choice\":\"Approve\", \"returnurl\":\"CALLBACKURL\"}"
                            }
                        ]
                    },
                    {
                        "@type": "ActionCard",
                        "name": "Reject",
                        "inputs": [
                            {
                                "@type": "TextInput",
                                "id": "comment",
                                "isMultiline": true,
                                "title": "Reason"
                            }
                        ],
                        "actions": [
                            {
                                "@type": "HttpPOST",
                                "name": "Submit",
                                "target": "FUNCTIONURL",
                                "body": "{\"comment\":\"{{comment.value}}\", \"choice\":\"Reject\", \"returnurl\":\"CALLBACKURL\"}"
                            }
                        ]
                    }
                ]
            },
            {
                "startGroup": true,
                "activitySubtitle": "Please complete the task."
            }
        ]
    }
}

Now add a “HTTP – HTTP Webhook” action

httpwebook

Choose the following:

  1. Subscibe – Method – POST
  2. Subscribe URI – the URL that can from the “Sending Workflow”
  3. Subscribe – Body – the Card variable. What we need to do at the point is replace the token CALLBACKURL with our Callback url, so the code is

    replace(variables(‘Card’), ‘CALLBACKURL’, listCallbackUrl())

The HTTP Webhook action is configured:

HttpAction

The flow should look like:

completedflow

Update the flow, and trigger it.

What should happen, is the card should be send to the “Sending Workflow” flow, which should then send you an email to the users in the “Users” list, and it should display in Outlook as an actionable card.

Next in the series is how to get the response back from the card, and continue your flow.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s