Contents
Newsletter robot
Enjoying the read?

Don't miss our next article

We all know that feeling with certain workplace apps. The despair of too many clicks, everything loading so slowly... as if this app was actively designed to make your life harder. 🤨😩

Even for modern apps it can be hard to please everyone, from a novice to a power user. Luckily, if you have even just a little development experience, you can create your own micro app that works just for you – using Adaptive Cards. In this case study, we'll take a practical look at a CRM, identify a pinch point with its UI and fix it with the power of an AI chatbot.

Related: Thinking of building with Adaptive Cards? 7 things you need to consider

Step 1: The problematic UI

I use Hubspot a lot in my job. It's a perfectly serviceable app that I have lots of good things to say about.

Screenshot of Hubspot CRM with a list of contactsHowever, the way we I personally use it, to qualify daily sales leads, is super tedious. Broadly, I start with a list of my new contacts that I need to qualify.

Unfortunately just looking at the people in my grid isn't enough for me to filter them, so I need to open each contact one at a time and look at their full record.

Clicking on each record takes a few seconds for it to load (somewhere around ~5-6 seconds), and a similar duration (~3-4 seconds) is spent when I return to my list.

Once on the record's page, the fun really begins.

Hubspot collects (de-personalized) usage metrics for how each contact uses our software. I hope I'm not boring you, but the process is that – in order to make our decision – I need to scan the sections in the middle and on the right (shown in green). In the best case, when all goes well and the contact is very interested, we have to make a total of four clicks (shown in yellow) to qualify the contact, kick off workflows, and return to the main list. Alternatively, I either message the lead directly if they're high profile, or delete them if they're junk (shown in purple).

What can be improved?

In a majority of cases, I only really use a small fraction of Hubspot's features. There is many dropdowns, tabs and sections that I don't usually need. So if I wanted to redesign this, I would take just the "activity" areas (shown in green) and put them on an Adaptive Card. Then I would put my most popular actions right beneath that, i.e. update status, delete or message.snag

Step 2: Making an Adaptive Card

Taking the above points on-board I created a wireframe of how I would design an ideal Adaptive Card for my use case. It looks like this:

Creating the Adaptive Card schema

Re-creating a mockup like this is actually pretty straight forward with the Adaptive Card designer. After a few minutes of work I came up with the below template. You can copy-and-paste this over to the Adaptive Card designer if you want to explore it for yourself.

Open an empty Adaptive Card

{
"type": "AdaptiveCard",
"version": "1.2",
"body": [
{
"type": "ColumnSet",
"spacing": "Medium",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "Image",
"url": "${thumbnail}",
"$when": "${length(thumbnail)>0}",
"width": "40px",
"spacing": "None",
"horizontalAlignment": "Center",
"style": "Person"
}
]
},
{
"type": "Column",
"$when": "${length(thumbnail)>0}",
"width": "stretch",
"height": "stretch",
"horizontalAlignment": "Center",
"spacing": "Small",
"items": [
{
"type": "FactSet",
"facts": [
{
"title": "${title}",
"value": "**${description}**"
}
],
"$when": "${if(title, true, false)}"
},
{
"type": "FactSet",
"facts": [
{
"title": "${email}",
"value": "**${description}**"
}
],
"$when": "${if(title, false, true)}"
},
{
"type": "TextBlock",
"text": "${requested}",
"wrap": true,
"fontType": "Default",
"size": "Large",
"spacing": "Small"
},
{
"type": "FactSet",
"facts": [
{
"title": "Created ${ago}",
"value": "Current status: ${lead_status}"
}
],
"spacing": "Small"
}
]
}
],
"selectAction": {
"type": "Action.OpenUrl",
"url": "${link}",
"id": "open-contact-${id}"
}
},
{
"type": "Container",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "180px",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "180px",
"id": "cmdTab1",
"items": [
{
"type": "TextBlock",
"text": "Activities",
"weight": "Bolder",
"size": "Medium",
"horizontalAlignment": "Center"
}
],
"selectAction": {
"type": "Action.ToggleVisibility",
"targetElements": [
"cmdTab1",
"cmdTab1Selected",
"cmdTab2",
"cmdTab2Selected",
"tab1",
"tab2"
]
},
"isVisible": false,
"separator": true,
"minHeight": "25px",
"horizontalAlignment": "Center",
"verticalContentAlignment": "Center"
},
{
"type": "Column",
"width": "180px",
"minHeight": "25px",
"items": [
{
"type": "TextBlock",
"text": "Activities",
"color": "Accent",
"id": "cmdTab1Selected",
"size": "Medium",
"weight": "Bolder",
"horizontalAlignment": "Center"
}
],
"verticalContentAlignment": "Center",
"selectAction": {
"type": "Action.ToggleVisibility"
}
}
],
"spacing": "Medium"
}
],
"verticalContentAlignment": "Center"
},
{
"type": "Column",
"width": "180px",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "180px",
"minHeight": "25px",
"items": [
{
"type": "TextBlock",
"text": "Details",
"size": "Medium",
"weight": "Bolder",
"horizontalAlignment": "Center"
}
],
"id": "cmdTab2",
"selectAction": {
"type": "Action.ToggleVisibility",
"targetElements": [
"cmdTab1",
"cmdTab1Selected",
"cmdTab2",
"cmdTab2Selected",
"tab1",
"tab2"
]
},
"horizontalAlignment": "Center",
"verticalContentAlignment": "Center"
},
{
"type": "Column",
"width": "180px",
"minHeight": "25px",
"items": [
{
"type": "TextBlock",
"text": "Details",
"color": "Accent",
"size": "Medium",
"weight": "Bolder",
"horizontalAlignment": "Center"
}
],
"id": "cmdTab2Selected",
"isVisible": false,
"horizontalAlignment": "Center",
"verticalContentAlignment": "Center",
"selectAction": {
"type": "Action.ToggleVisibility"
}
}
],
"spacing": "Medium"
}
],
"verticalContentAlignment": "Center",
"height": "stretch"
}
]
}
],
"spacing": "Medium",
"verticalContentAlignment": "Center",
"horizontalAlignment": "Center"
},
{
"type": "Container",
"id": "tab1",
"spacing": "Medium",
"items": [
{
"type": "TextBlock",
"text": "🔌 ${active_integrations} active integrations",
"wrap": true,
"size": "Large",
"$when": "${active_integrations != ''}"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "50px",
"items": [
{
"type": "TextBlock",
"text": "Time",
"wrap": true,
"horizontalAlignment": "Right",
"weight": "Bolder",
"isSubtle": true
}
],
"verticalContentAlignment": "Center"
},
{
"type": "Column",
"width": "49px",
"backgroundImage": {
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAYCAIAAAC0rgCNAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTZEaa/1AAAAEElEQVQYV2NoaGigHW5oAADAuiQBWxz13QAAAABJRU5ErkJggg==",
"fillMode": "RepeatVertically",
"horizontalAlignment": "Center",
"verticalAlignment": "Center"
},
"items": [
{
"type": "Image",
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwQAADsEBuJFr7QAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xNkRpr/UAAAAbUExURf///4CAgICAgICAgICAgICAgICAgICAgICAgK5UnXcAAAAIdFJOUwBMVIePw/f799S5VQAAAD9JREFUGNNjYKAtYAxLFYBzLDo6muESHUAAk2IFcQKgHDYQJwHKYQdxCrApYwJxFGDGeXR0tMDtYQovVaCxvwAJCxPcs9XnIAAAAABJRU5ErkJggg==",
"width": "35px",
"spacing": "None"
}
],
"spacing": "None",
"bleed": true
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "Activity",
"wrap": true,
"weight": "Bolder",
"isSubtle": true
}
],
"verticalContentAlignment": "Center"
}
]
}
],
"minHeight": "150px"
},
{
"type": "Container",
"id": "tab2",
"isVisible": false,
"spacing": "Medium",
"items": [
{
"type": "TextBlock",
"text": "📇 Contact details",
"wrap": true,
"weight": "Bolder"
},
{
"type": "FactSet",
"facts": [
{
"title": "Email",
"value": "${$root.email}"
}
]
},
{
"type": "TextBlock",
"text": "🌎 Origin",
"wrap": true,
"weight": "Bolder"
},
{
"type": "FactSet",
"facts": [
{
"title": "${source_label}",
"value": "${source}"
},
{
"title": "${ip_country_label}",
"value": "${ip_country}"
},
{
"title": "${first_page_seen_label}",
"value": "${first_page_seen}"
}
]
}
],
"minHeight": "150px"
},
{
"type": "ActionSet",
"actions": [
{
"type": "Action.ToggleVisibility",
"title": "Update status",
"targetElements": [
"lead-update-status-${id}"
]
},
{
"type": "Action.Submit",
"title": "Delete",
"id": "delete"
},
{
"type": "Action.OpenUrl",
"title": "Message",
"url": "${link}/?interaction=email"
}
]
},
{
"type": "Container",
"isVisible": false,
"id": "lead-update-status-${id}",
"style": "emphasis",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "Input.ChoiceSet",
"choices": [
{
"title": "New",
"value": "New"
},
{
"title": "Contacted",
"value": "Contacted"
},
{
"title": "Interested",
"value": "Interested"
},
{
"title": "Under Review",
"value": "Under Review"
},
{
"title": "Demo",
"value": "Demo"
},
{
"title": "Convert",
"value": "Convert"
},
{
"title": "Unqualified",
"value": "Unqualified"
}
],
"placeholder": "Update Lead status to",
"id": "status"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "ActionSet",
"spacing": "Small",
"actions": [
{
"type": "Action.Submit",
"title": "Update",
"id": "update"
}
]
}
]
}
]
}
],
"bleed": true
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}

The Adaptive Cards looks like this in the flesh.

Digital Assistant Cards showing lead information from Hubspot CRMThere is even a row of in-line actions along the bottom that let you either update the status of the contact, delete them or send them an email.

Inline actions on the Hubspot CRM Digital Assistant Card to update a lead's status, delete the lead, or send a messageYou can even drag in more elements from the sidebar and customize the layout, if you want.

There's just one small problem still: the above template Card isn't yet showing any live data from my actual Hubspot account. That's what we'll take care of in the next step.

Step 3: Connecting the Adaptive Card to the API

Like almost every SaaS app, Hubspot offers an API. This allows you to read out and write back into your Hubspot data in a way that's secure, fast and standardized across the industry.

We've already got a working Adaptive Card, so how do we get a live feed set up?

Just go over to the App Directory page and look for the application you're trying to connect to. In this case for Hubspot just hit Install now which will redirect you to the Digital Assistant Board (and ask you to login if you aren't already).

Some apps, such as Hubspot here, already have built-in Cards that get installed. But you can just ignore these and return back to the Adaptive Card design you still have open.

Back in the Designer hit Save and then refresh the Designer. Now you can click on Connect to data source and select Hubspot from your dropdown list - and perhaps adjust the request URL and scopes to your needs.

Once you save that, you may be prompted to carry out the account linking.

Full view of Digital Assistant's Adaptive Cards DesignerHaving selected this Connector, it will now start to project Hubspot's live data into the Sample Data Editor section.

Step 3.5: Data binding

We now use a process called data binding to marry together the layout of the Adaptive Card with the live data from Hubspot. The layout from above should already work somewhat with Hubspot's API, but it's worth pointing out the inner workings – in case you wanted to apply this to your own case.

To bind our data we start by looping our ColumnSet. We do this simply by adding the following code to line 7: "$data": "${$root.contacts}",

This tells the designer to show such a ColumnSet for each entry in the "contacts" array of our sample data JSON.

You know this worked successfully, when you click on Preview Mode and the entries suddenly multiply by however many items are in your API sample. If it didn't work, check out the docs for templating and see if you can debug your Card payload.

Finally, now we can bind the actual data from the sample response we copied to our design.

First we navigate to the the element where we want to show the name and we enter ${properties.firstname.value} ${properties.firstname.value}:

This tells the Card designer to look for the correct entry in the JSON by navigating through the "tree" and delving deeper into every branch denoted with a . in the binding command.

In our example the API's root is already set to the contacts array (defined in line 7), so to get to the first name of each contact the Card Designer needs to:

  • Go to the properties array

  • Within there, find the firstname object

  • Read out the value nested within

We follow the same logic for the last name, and the company name below that.

If everything worked, your Card should now show each contact separately.

Step 4: Train the chatbot to find your Adaptive Card

Once that is done you can just go to your Board where the Card should just wait for you. Alternatively you could teach the Card some AI training phrases by clicking on Add training phrases in the Designer first.

Training phrases are like a catalog of ways the users could ask for this Card with natural language. Even if you train it just a few different variants, like What are my sales leads? or Show me my Hubspot contacts, then you could ask your chatbot for the Card.

Microsoft Teams chat with Digital Assistant, with a response displaying the Hubspot CRM CardThe chatbot is available for many different applications such as MS Teams or your Intranet. The Digital Assistant app built-in with Microsoft Teams for example will show you the Card when you ask it one of the Utterances you pre-trained the Adaptive Card with.

You could even embed this Card into SharePoint from the Adaptive Card Designer's Share menu.

Bottomline

Even though the use case for this is, of course, very specific to Hubspot, it shows you the strengths that making your own chatbot offers. It's a simple way to connect various applications to a common interface, and due to Adaptive Cards allows you to customize interactions and layouts completely so they suit the use case at hand.

The number of clicks we talked about in the beginning (to open individual contacts' records, as well as within the contacts page to qualify them) has been dramatically reduced. The number of tabs or page refreshes necessary to work through contacts has gone from several dozens down to zero.

All-in-all, even though it may not be a perfect solution, it was a great improvement for our team and took us all of an afternoon to create, which is more than made up for through the time savings in the future.

~

I hope this guide showed you how easily you can improve the user flow for your applications and make your power users more productive and efficient. Is there a use case you would like to simplify? What challenges do you face that an Assistant could help your users master? Let me know in the comments below.

Author portrait

Article by Henry Amm

I’m the Senior Director for the Digital Assistant Platform. Prevously gained 6 years of experience as an Intranet consultant. Fluent in German.

Make your Assistant Build your free AI-powered Digital Workplace platform in minutes, with our free forever plan. Digital Assistant logo Yes please
Author portrait

Article by: Henry Amm

I’m the Senior Director for the Digital Assistant Platform. Prevously gained 6 years of experience as an Intranet consultant. Fluent in German.

Join the discussion

Avatar placeholder
This field is required.
This field is required.
This field is required.

0 responses

Be the first to leave a comment!