ODC with Loop Components - Extend to Outlook


In the ODC with Loop Components series so far, we have been using Microsoft Teams for our Adaptive Card-based Loop Components. By now, you should have been able to unfurl an auction link from the demo application into an Adaptive Card in a Teams conversation and interact with it by placing a bid.
If you try to copy and paste an auction link—either by copying the link from the browser or using the Copy component icon on a card—into a new Outlook message, you'll notice it doesn't work yet, meaning the URL won't unfurl. To extend our sample to support Microsoft Outlook messages as well, we need to do two additional steps:
Add the Microsoft 365 channel configuration in our Azure AI Bot resource in the Azure Portal.
Add a Search Command to our App Manifest.
(Optionally) Implement the configured Search Command in our OutSystems Developer Cloud application.
Demo Application
This article series includes a demo application called "ODC with Loop Components Demo," available on ODC Forge. Be sure to download the version of the application that matches each article in this series.
For this article, you need to install Version 0.4 from ODC Forge.
In the ODC Portal, go to Forge - All Assets.
Search for "ODC with Loop Components Demo".
Click on the Asset (Do not click on Install on the tile!).
Switch to the Version tab and click on Install next to Version 0.4.
Version 0.4 depends on other Forge components:
OAuthTokenExchange - An external logic library that helps retrieve access tokens easily. In this tutorial however we use an action to retrieve the discovery document from Microsoft Entra only.
LiquidFluid - An external logic library that merges data with templates based on the Shopify Liquid Templating language.
UriParser - An external logic library that parses a given URI/URL.
What we build
In this part, we extend our implementation to allow pasting auction links into Outlook messages, which are then expanded into Adaptive Card-based Loop components.
The good news is that we don't need to change our implementation. We only need to configure the Azure AI Bot resource in the Azure Portal and make one addition to our App Manifest by adding a Search Command. After that, our card will work just like it does in Teams, including card refreshes and, of course, the ability to place a bid.
Activate Microsoft 365 channel
This task is straightforward. Open the Azure Portal and select your Azure AI Bot Resource (if you followed the tutorial, its name should be BotId-<Application (client) ID>).
At the resource level, select Settings - Channels and under Available Channels, click the Microsoft 365 channel entry.
Click the Apply button to activate this channel.
Modify App Manifest
Activating the Microsoft 365 channel extends the support of our Azure AI Bot resource to Microsoft 365 applications.
However, when you try to paste an auction link into a new message, it still doesn't expand into an Adaptive Card. It took me a while to figure this out because the documentation isn't very clear, but for Microsoft Outlook to support link unfurling, your App Manifest must have at least one command configured.
In an App Manifest, you can configure two types of commands:
Query - Allows a user to perform searches. Query implementations return search results from which the user can select.
Action - Allows a user to perform any action, like creating a task in Microsoft To-Do directly from the conversation or message.
For link unfurling into an Adaptive Card, we only need to define one command in the App Manifest. However, we don't need to implement the command (though we will look at a sample implementation later in this tutorial).
Open your manifest.json and replace the empty commands array with the following:
{
...,
"commands": [
{
"id": "searchAuction",
"type": "query",
"title": "Search Auction",
"description": "Searches available auctions",
"initialRun": false,
"fetchTask": false,
"context": ["compose", "commandBox"],
"parameters": [
{
"name": "searchTerm",
"title": "Search Term",
"description": "Part of the auction title to search for",
"inputType": "text"
}
]
}
],
...
}
This defines a query command called searchAuction that takes one input parameter, searchTerm, and is available in the Microsoft Outlook new message window and the Microsoft Teams command bar (context).
Additionally, increase the app manifest version
property.
After making the changes, compress the manifest.json file along with the icons into a zip file. Then, re-upload the custom app to Microsoft Teams as described here.
Try it
Even without implementing the query command, the link unfurling of an auction should now work without any extra setup.
Open Microsoft Outlook and click on New - Mail in the ribbon menu. In the message compose window, click on Apps in the ribbon menu. Check that the app list includes the ODCwithLoop app.
Copy an auction link from the demo application and paste it into the message body. After pressing return, the link should successfully unfurl to our auction card, just like in Microsoft Teams.
With link unfurling working, we can now move on to implementing the query command in our application. This step is optional if link unfurling is the only feature you need. However, since we defined a query command, it's available in both the Outlook and Teams interfaces. It would create a poor user experience if someone tries it and finds that it doesn't return any results.
Search Auction Command
Our defined Search Auction command is available in both Microsoft Teams and Microsoft Outlook.
In Teams, you can find it in the conversation command bar by clicking on the + icon and searching for ODCwithLoop. After clicking on the app, you will see the following screen.
In Outlook, it is available in the compose message window. Click on Apps and select the ODCwithLoop app from the list.
Whenever you start typing our messaging endpoint receives a request of type invoke
and a name of composeExtension/query
. This request contains a value property that looks like this:
{
...,
"value": {
"commandId": "searchAuction",
"parameters": [
{
"name": "searchTerm",
"value": "pot"
}
],
"queryOptions": {
"count": 25,
"skip": 0
}
}
...
}
commandId - the value defined for the command id in the app manifest.
parameters - an array of parameter key/value pairs as defined in the app manifest.
queryOptions - the default query options count (max items to return) and skip (skip number of records.
The response to a composeExtension/query
must be a list of cards supported by the search widget. At the time of writing, the search widgets in both Teams and Outlook support:
Hero Card - A card that usually has a large image, one or more buttons, and text.
Thumbnail Card - A card that usually has a thumbnail image, one or more buttons, and text.
Both card types are only for displaying the search result list. The user can click on an entry to add the card to a Teams conversation or an Outlook message. To add the full card, we need to include some extra configuration and logic to retrieve the full card from the messaging endpoint once a user clicks on an entry.
Handling Search
When a user starts typing in the search widget, an activity request is sent to the messaging endpoint. This request is of type invoke
and has the name composeExtension/query
. It includes the searchTerm in the value object, along with other configurations (see the sample above).
Open ODC Studio and go to Logic - Integrations - REST - MessagingEndpoint, then open the Messages flow.
Inspect the third condition (query) of the Invoke switch.
DeserializeQueryCommandActivity - Deserializes the request containing the value object that contains the searchTerm parameter.
FilterSearchTerm - The searchTerm is part of the parameters array and we need to filter the list to get it.
MessagingSearchAuctionAction
This action performs a query on the Auction entity, constructs a payload structure and renders the search result template.
SearchAuctions - Queries the Auction entity.
GetDefaultDomain - Retrieves the domain name of the current ODC stage. This is needed to build the full URL to the auction.
SearchAuctions.List and AppendAuction - Adds the search results to a local payload structure and constructs the auction URL.
RenderAuctionCardListResponse - Renders the response using the payload data.
RenderAuctionCardListResponse uses the following Liquid template:
{
"composeExtension": {
"type": "result",
"attachmentLayout": "list",
"attachments": [
{% for auction in auctions %}
{
"contentType": "application/vnd.microsoft.card.thumbnail",
"content": {
"title": "{{auction.title}}",
"text": "{{auction.description}}"
},
"preview": {
"contentType": "application/vnd.microsoft.card.thumbnail",
"content": {
"title": "{{auction.title}}",
"text": "{{auction.description}}",
"tap": {
"type": "invoke",
"value": {
"url": "{{auction.webUrl}}"
}
}
}
}
}
{% endfor %}
]
}
}
For each auction in the payload structure, an attachment is created. The attachment includes the same thumbnail card content and preview, both needed for message extension responses, with one difference. The preview card has an extra object called tap. This object specifies what should happen when a user clicks on the card, such as when selecting an entry in the search widget. In our setup, it should invoke the messaging endpoint and send a parameter url, which contains the full URL to the auction.
This will send a request activity to our messaging endpoint with the type invoke
and the name composeExtension/selectItem
. We need to handle this next to return the full auction card.
Handle Item Selection
Back in the MessagingEndpoint - Messages flow, check the fourth Invoke switch condition. This condition handles the composeExtension/selectItem
request.
DeserializeSelectItemActivity - Deserializes the request into a structure that includes the url parameter of the selected entry.
CreateFullCardForSelectedItem - Executes the MessagingQueryLinkAction server action, which we also use to respond to a link unfurling request.
With everything set up, you can now try it out. Use the search widget in either the Teams or Outlook app to look up an auction. Click on an entry to add the full card to the conversation or message.
I recommend debugging through the flows to check the different results.
Summary
In this tutorial, we set up our Azure AI Bot resource to work with Microsoft Outlook, in addition to Microsoft Teams, for our Adaptive Card-based Loop Component demo. We then updated our app manifest and added a search command, which is necessary for it to function in Microsoft Outlook. Finally, we implemented the search command, although it is optional if only link unfurling is needed. We learned that we need one logic to handle search requests and another to return a full card when a user clicks on a search result.
For now, this is the final tutorial in the ODC with Loop Components series. There are certainly more topics to explore, but this series should provide a good starting point. I hope you enjoyed working through it. Feedback is always appreciated.
Subscribe to my newsletter
Read articles from Stefan Weber directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Stefan Weber
Stefan Weber
As a seasoned Senior Director at Telelink Business Services EAD, a leading IT full-service provider headquartered in Sofia, Bulgaria, I lead the charge in our Application Services Practice. In this role, I spearhead the development of tailored software solutions using no-code/low-code platforms and cutting-edge cloud-ready/cloud-native solutions based on the Microsoft .NET stack. Throughout my diverse career, I've accumulated a wealth of experience in various capacities, both technically and personally. The constant desire to create innovative software solutions led me to the world of Low-Code and the OutSystems platform. I remain captivated by how closely OutSystems aligns with traditional software development, offering a seamless experience devoid of limitations. While my managerial responsibilities primarily revolve around leading and inspiring my teams, my passion for solution development with OutSystems remains unwavering. My personal focus extends to integrating our solutions with leading technologies such as Amazon Web Services, Microsoft 365, Azure, and more. In 2023, I earned recognition as an OutSystems Most Valuable Professional, one of only 80 worldwide, and concurrently became an AWS Community Builder.