As the fastest growing business app in the history of our company, Microsoft Teams is gaining a strong foothold with our Office 365 enterprise users. Millions of enterprise employees are now starting their day in Teams, living their day in Teams and wrapping up their day in Teams. One of the core value drivers within this productivity workspace is its extensible and customizable platform – essentially bringing modern SaaS app experiences within the scaffolding of the product itself. Thanks to 250+ apps on the public app store and countless more on private, organization-specific app catalogs inside Teams, it’s natural for a typical user to expect her organizational tools and most importantly, common workflows to be accessible inside Microsoft Teams.
Broadly speaking, there are 2 methods of surfacing your organizational workflows inside Microsoft Teams:
- Code from scratch (CFS) – This is where you go all in and get your enterprise developers to write a web application complete with backend database, a frontend experience custom-built for Teams and APIs to read/write information for user to view or take action on
- Low code / no code (LCNC) – This is where you keep it lightweight and quick and get literally anyone eg: administrators, business owners or in general, people who are not full-time software developers to build and configure functional workflows inside Teams.
LCNC democratizes creating and surfacing workflows as “apps” inside Teams – enabling (a) a modern end-user experience in addition to (b) low cost and time of workflow development. During our evangelism and consultative work with some of the top enterprise customers using Microsoft Teams today, approvals is one such kind of workflow that seems to exist universally.
Let’s see how you can build a simple approval workflow the LCNC way using just SharePoint Online + Microsoft Flow + Microsoft Teams, and nothing else - no web browser, no email, no Microsoft Flow mobile application etc. participating in the mix. The entire workflow will begin and end for all participants within Microsoft Teams itself – resulting in minimum context switching and maximum productivity.
The Problem Statement
Let’s imagine that you manage the Travel Desk for your company. Your team arranges home drops via a company-owned shuttle service to employees staying late in the evening beyond regular transport hours. You want to surface a shuttle request workflow within Microsoft Teams whenever a new drop request is made by an employee. One of your Travel Desk team staff needs to look into the request, validate against policy and then approve it. The requester employee must get notified with the status of their request in Teams.
Setting up the Components
- SharePoint portal: We’re using a simple, modern SharePoint site public to the entire org titled ‘Vishrut's Workgroup’ for this blog post.
- SharePoint list: Within the SharePoint site, a simple, public list titled ‘Cab Request’ has been created. All requests from employees will be collected and actioned upon in this list.
Employee team in Microsoft Teams: A team titled Vishrut’s Workgroup has been created by teamify-ing the SharePoint site created above. This will make it easier to pin the list as a tab since the list is part of the team’s corresponding SharePoint in backend.
- Pin the list as a Channel Tab: Add the SharePoint list as a channel tab in the Staying Late channel of the Vishrut’s Workgroup In real-world, you can expose the list as a SPFx web part inside any Teams channel as a tab.
- Travel Desk team in Microsoft Teams: The entire travel desk team is part of the Travel Desk team within Microsoft Teams. Those who can approve employee shuttle drop requests follow the Cab Requests channel to get notified.
Designing the Approval Flow
This is how you – as the manager of the Travel Desk – design a simple approval flow for approvers on your team:
Notifying about a New Approval Request
Here’s how you set it up using connectors in Microsoft Teams and trigger/action in Microsoft Flow:
- Add an Incoming Webhook connector to the Cab Request channel in the Travel Desk team. Configure the connector as follows and obtain the webhook URL provided in the dialog. Copy the webhook to the clipboard and save it. You'll need the webhook URL for sending information to Microsoft Teams.
- In Microsoft Flow, setup a new “When an item is created -> HTTP” flow i.e. choosing to invoke a REST API whenever an item is created in a given SharePoint list (Cab Request in this case). Use the webhook URL obtained in the previous step in the HTTP action’s URI field. By doing so, you’re piping the request raised in SharePoint list into the Cab Request channel.
Now, let’s compose the connector card in message card format (which Microsoft Teams supports). While designing the connector card, you can head over to the Card Playground which allows you to see what your card will look like as you edit the associated JSON payload. The card payload goes in the Body of the HTTP action on the Flow page.
You can add dynamic content available from the SharePoint list in previous step (in context) to show up on the connector card by inserting them directly into the JSON payload in the Body compose box. A reference JSON payload in available on GitHub to get you started. Save the Flow to commit your trigger/action.
Test out the Flow you just created by adding a new item in the SharePoint list from the channel tab in Staying Late channel as follows:
You should see a rich Connector card appear in the Cab Requests channel as follows:
This is powerful since your Travel Desk team now has the request – from an external system - appearing directly within Microsoft Teams where they can collaborate around it. For example: you can @mention a specific team member of yours to check whether the requesting employee’s can avail the shuttle drop facility by company policy or not.
Taking Action on an Approval Request
Notice the Approve and Reject action buttons on the connector card posted into the channel. Your approver can decide to action on the request and even add their comments. The JSON payload for the card you specified in the Body field of the HTTP action contains the actions. Here’s the snippet that defines what should happen when the user chooses the action Approve.
The most important piece in the JSON code above is the “target” value specified which defines the URL endpoint of the service that receives and then acts on the action made by the user (your approver in this case). At a high level. this is what happens when the approver hits Submit on the card:
This means the target URL is basically an Internet-reachable endpoint which can receive action data. So how do you obtain the target URL for your approval flow?
Obtaining the Target HTTP URL
This is the essence of our low code / no code solution. Usually, target URLs need a web service to be run and exposed on the Internet. However, Microsoft Flow’s trigger “Request — When a HTTP request is received” does exactly this. The trigger generates a HTTP POST URL, which can trigger a Flow whenever an incoming API call hits this target. Therefore, we setup another Flow with this trigger.
We will also need a JSON schema defining the format of the request that will hit this HTTP target endpoint. To simplify schema creation, you can use one of many schema generators available on the web for eg: jsonschema.net. A reference schema for the connector card is available on GitHub to get you started.
The HTTP POST URL will get generated once you Save the Flow.
This URL needs to be picked up and inserted as the target URL in the Connector card’s JSON Body field defined in the first Flow you’ve setup. Note that the same target URL is specified in case of both actions – Approve and Reject.
Only the Boolean values contained in the ‘approved’ key as part of the HttpPOST body section differentiates between either decision. In the section titled “Updating the SharePoint list item” below, we use the Boolean to populate Request Status Value field in the SharePoint list item.
Updating the SharePoint list item with action data
The subsequent actions for this Flow look like the following:
Basically, the remaining steps of the Flow deal with receiving the HTTP payload coming to the target URL, parsing the same, extracting required information such as the action taken, comments and the identify of the person who’s taken the action in order to store it in the same SharePoint list. This is how it is achieved:
- Parsing the HTTP Header: Using the Parse JSON action available in Flow, the HTTP Header (dynamic value from previous step) is parsed. A reference schema for the HTTP request header is available on GitHub to get you started.
- Parsing the Action-Authorization field in the request header: The Action-Authorization field in the header for actionable messages contains the bearer token which contains the identify of the user who has taken the action. The token is obtained using a split expression “split(body('Parse_JSON')?['Action-Authorization'],'.')” in Flow. We’re splitting the bearer token at ‘.’, placing the contents into an array and picking the 2nd element from the array to obtain the JWT token which includes the Azure AD identity of the Office 365 user who took the action. If you wish to decode a JWT token to see it’s fields, use the https://jwt.io/#debugger A reference schema for the JWT token is available on GitHub to get you started.
- Obtain identify of the user performing the action: If you go through the Connector documentation, you’ll see that the ‘sub’ parameter contains the Azure AD object ID of the user who took the action. We use an existing User connector in Flow that makes is easier to get user information basis this object ID.
- Updating the SharePoint list item: Now that we’ve all the information extracted from the action taken by the Travel Desk team member who reviewed this shuttle request, it’s time to update the SharePoint list item with the required info i.e. the decision/status of the request, the comments of the reviewer and the identify of the reviewer.
This is achieved by first obtaining the corresponding SharePoint list item using the ID passed into the incoming HTTP request. You may recall that we’d posted the ID specifically in the action body when composing the Connector card JSON itself. The ID passed plays the role of stitching two completely separate Flows you’ve set up. Using the ID passed in the first Flow, you can update the same list item in second Flow.
You can now update the list item accordingly. The Request Status Value field is populated using an expression “if(triggerBody()?['approved'],'Approved','Rejected')”. Note that this expression is based on the ‘Approved’ boolean’s value from the JSON schema of this Flow’s trigger (When a HTTP request is received). The Comments field is simply populated from the corresponding value in the incoming HTTP request.
Closing the Loop: Feedback in Channel
It is imperative to give both the reviewer and the requesting employee feedback once the approval is executed.
- Feedback for the reviewer: The reviewer and the rest of your Travel Desk team needs to know who’s reviewed a particular shuttle drop request and the outcome. That is why the final step of the 2nd Flow uses the HTTP Response action as follows:
As per the Connector documentation, you can refresh the Connector card in the Cab Request channel once the reviewer takes action to fully update the card on the fly. The approved card captured the status of the request, the reviewer (in this case I’ve approved my own request!) and a reply message. Notice the updated card does not contain the action buttons. This is very powerful to ensure no other Travel Desk team member can take further action on this request.
To refresh a card as a result of an HttpPOST action, the JSON payload of the new card is included in the Body section of HTTP Response. You’ll also need to add CARD-UPDATE-IN-BODY: true HTTP header to the response. The reply to show in the channel conversation is returned in the CARD-ACTION-STATUS header in HTTP Response.
The refreshed Connector card also uses dynamic values thanks to Flow to compose the content. A reference schema for the HTTP Response’s Body section containing the refreshed Connector card is available on GitHub to get you started.
- Notifying the Employee who requested the shuttle drop: The employee can be notified over email instantly using Send email action that Flow provides.
The requesting employee receives an email as follows:
We hope this hands-on blog post has enabled you to set up your first LCNC approval workflow in Microsoft Teams using just SharePoint Online + Microsoft Flow, Connectors in channels and nothing else - no web browser, no email, no Microsoft Flow mobile application etc. Happy approving!
Special thanks to Wajeed Hanif Shaik - our Microsoft Teams platform developer support engineer who was able to get this LCNC approval workflow up and running in a matter of a few hours!