Bots & Custom Apps
Adaptive cards in Microsoft Teams, an introduction
Adaptive Cards show up in Microsoft Teams like polite interceptors. They carry a small payload of information and maybe one or two actions, then get out of the way. When people first see a card, they often ask for twelve fields and three menus. That is how a clear status update becomes a tiny failed form.
I am Priya Shah, a Microsoft Teams automation consultant. This introduction is educational and independent. I am not a Microsoft partner, and Microsoft does not endorse these examples. Think of cards as structured messages your flows and bots can post, not as a full application framework.
What an Adaptive Card actually is
An Adaptive Card is a JSON document that describes a lightweight UI: text, simple inputs, containers, and actions. Hosts like Microsoft Teams render that JSON into a visual card inside chat or a channel.
You author the structure. The host controls look-and-feel boundaries. That split is why the same card can appear in more than one Microsoft surface with differences. Design for Teams first if Teams is your primary host, and test there.
Cards are ideal for status, approvals lite, confirmations, and short requests for information. They are a poor place to rebuild your entire CRM screen.
Under the hood you will meet schemas, versions, and action types. You do not need to memorize every property to ship a useful status card. You do need to keep the JSON valid and the action data clear.
I tell beginners to think in newspaper terms. Headline. Two facts. One ask. If you cannot fit the job into that shape, you may need a List form, a Forms form, or a custom tab instead of a card.
A short status card JSON example
Here is a minimal status card shape I use when teaching. It shows a title, owner, status, and one submit action. It is intentionally small.
{"type":"AdaptiveCard","$schema":"http://adaptivecards.io/schemas/adaptive-card.json","version":"1.5","body":[{"type":"TextBlock","text":"Campaign status","weight":"Bolder","size":"Medium"},{"type":"FactSet","facts":[{"title":"Title","value":"Spring launch assets"},{"title":"Owner","value":"Jordan Lee"},{"title":"Status","value":"In review"}]}],"actions":[{"type":"Action.Submit","title":"Acknowledge","data":{"action":"acknowledge","itemId":"cmp-1048"}}]}
Read it top to bottom. A title TextBlock. A FactSet for the three fields. An Action.Submit that sends a small data object back to whatever posted the card, often a Power Automate flow waiting for the submit.
You can pretty-print this in an editor when you build. I collapsed it here so you can see the whole idea in one breath. The important part is the data payload. Without itemId or an equivalent key, your flow will not know which record the click belongs to.
Do not paste secrets into the card JSON. Anything in the card may be visible to users who can see the message.
How Power Automate posts a card into Teams
At a high level the pattern looks like this. A trigger fires: a List item changes, a Form arrives, a schedule runs, a webhook lands. The flow composes Adaptive Card JSON, often with dynamic fields from the trigger. Then a Teams connector action posts the card to a chat or channel.
For interactive cards, you typically use an action that waits for the submit or handles the invoke according to the connector pattern your tenant supports. The flow then branches: update the source item, post a confirmation, notify someone else.
Keep the flow and the card template versioned together. When someone edits the JSON in one flow but not the twin flow in another environment, users see inconsistent buttons and lose trust.
Post to a channel that expects operational messages. Do not drop status cards into a social channel where people share Friday photos. Context switch fatigue is real.
Test with a sandbox team. Submit the button yourself. Confirm the List row updates. Confirm what the user sees after submit. Many cards feel unfinished because the post-submit state still shows the old button with no confirmation.
- Trigger with a clear source record id.
- Compose card JSON with that id in Action.Submit data.
- Post to the right Teams scope.
- Handle submit by updating the source and confirming to the user.
Two fields you should not add yet
First, do not add a free-text “notes” input on version one of a status acknowledgement card. Notes feel harmless. They create moderation questions, longer mobile layouts, and incomplete updates where people type novels or leave the box empty with no rule. Let acknowledgement be a single click. If you need comments, open a thread reply or a dedicated form after.
Second, do not add a priority dropdown until the team shares a written definition of priority values. I have watched cards collect P1 through P4 with no shared meaning. The dropdown becomes emotional decoration. Teach the status vocabulary first. Add priority later if reporting requires it.
Those two omissions keep the card honest. Title, owner, status, acknowledge. That is enough to change a team’s daily rhythm.
If stakeholders insist on more fields immediately, schedule a second iteration date. Do not negotiate the first card into a form during the demo meeting. Demo meetings are where cards go to overgrow.
Design habits that keep cards readable
Lead with the verb the reader needs. “Approve discount” beats “Request.” Put the rare important number near the top. Keep FactSet titles short. Avoid repeating the channel name inside the card unless the card might be forwarded.
Use one primary action. Secondary actions are fine when they are truly secondary, like View item opening a URL. Too many submits confuse which path the flow will take.
Write failure text in the flow for expired cards or duplicate submits. Users double-click. Networks retry. Your flow should tolerate that without creating duplicate List rows.
Accessibility matters even in chat. Clear titles and meaningful action labels help. “Submit” alone is weaker than “Acknowledge status.”
Concrete example. A support operations lead wanted a card for every new high-severity ticket. Version one had eight inputs including notes, priority, customer mood, and a tag picker. Nobody completed it on a phone. We cut to title, owner, status, and Acknowledge. Completion jumped. Two weeks later we added a View ticket URL action only. Notes stayed in the ticket system, which is where notes belonged.
Common mistakes I see in the first month
Posting cards without a stable record id in the submit payload. Building three different visual styles for the same process. Forgetting that card recipients need permission to the underlying List or link target. Using cards for long data entry. Treating a successful post as a successful process when nobody monitors failed flow runs.
Another mistake: editing a live card template while a wait-for-submit run is open. People click an outdated shape. Keep template changes coordinated with flow versions.
Also watch fan-out. A flow that posts a card to twenty channels for one event will train people to ignore cards. Target narrowly.
- One purpose per card.
- One primary action.
- Stable identifiers in submit data.
- Sandbox test on desktop and mobile.
- Flow failure alerts to an owner.
Where cards sit relative to apps and bots
A card can be posted by a flow with no bot. A bot can also send cards as part of a conversation. A custom app can embed richer experiences when cards are too small.
Start with flow-posted cards when the trigger is an event and the interaction is short. Add a bot when users must summon help on demand. Add a custom app when you need workspace depth.
Adaptive Cards are a format for brief structured work inside Teams. Keep the JSON small, let Power Automate post and handle Action.Submit, and refuse the early urge to add notes and undefined priority fields. Master the status card first. Everything fancier gets easier after people trust the simple one.
When you are ready for a second card template, copy the first and change only what the new process requires. Shared FactSet patterns and shared action naming make support easier. If every team invents a different acknowledge button label, your flow conditions become a forest of special cases. Consistency is not bureaucracy here. It is how you keep the automation readable six months later.
I also recommend a tiny catalog note in your team wiki: card name, purpose, source flow, source List, and the owner who can edit the JSON. Five lines. That note prevents the scavenger hunt that starts when a button stops working and nobody remembers which flow posts the card. Cards feel lightweight until something breaks on a Monday morning.
Start tomorrow with one status card in a sandbox channel. Title, owner, status, Acknowledge. Wire the submit to update a List row. Watch someone use it on a phone. Then decide what, if anything, deserves a second field. That sequence beats a week of debating the perfect schema before a single card appears in Teams.