# Adaptive Card based Loop Components in Microsoft 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745315251521/1d5b7d2d-2150-4606-931f-53c7f3cec29b.png align="center")

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](https://learn.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/search-commands/define-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](https://portal.azure.com) and select your **Azure AI Bot** Resource (if you followed the tutorial, its name should be BotId-&lt;Application (client) ID&gt;).

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:

```json
{
  ...,
  "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](https://without.systems/odc-with-loop-components-basic-setup-and-link-unfurling?source=more_series_bottom_blogs#heading-create-app-manifest).

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">After uploading, make sure to <strong>restart Microsoft Teams and Microsoft Outlook</strong>.</div>
</div>

# 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745321563428/1d33186d-0c90-48a3-ad64-0465e78794f9.png align="center")

In Outlook, it is available in the compose message window. Click on Apps and select the ODCwithLoop app from the list.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745338411817/6934a98e-e7a8-4ff3-819c-45e2ef525245.png align="center")

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:

```json
{
  ...,
  "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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745376019933/2342bef3-5e94-4c0c-a0e1-e8408d42a23c.png align="center")

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745376387963/5752d62a-67e8-46bb-8d4e-7be686347be3.png align="center")

* **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:

```json
{
    "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.

%%[follow-linkedin-button]
