Adaptive Cards “for everything”: one mental model, multiple hosts (Teams, Power Automate, Copilot Studio, SharePoint/Viva)

Adaptive Cards are authored in JSON and rendered by a host (Teams, Bot Framework, Copilot Studio, etc.). The host controls feature support and final UI, so portability is always about choosing a schema/features that all your hosts can render reliably.

Below is a step-by-step, exhaustive playbook to build cards that work across:

  • Teams bots / message extensions
  • Power Automate approvals & “post adaptive card and wait for a response” patterns
  • Copilot Studio
  • SharePoint / Viva Connections (especially Adaptive Card Extensions in SPFx)

Step 1 — Build a “lowest-common-denominator” baseline card

When you need “one card to rule them all,” start with these building blocks:

Safe body elements (broad support)

  • TextBlock
  • Container
  • ColumnSet / Column
  • Image (simple)
  • FactSet (useful but not always best UX)

These are widely used and are the most stable across hosts.

Safe actions (broad support)

  • Action.OpenUrl (most portable link pattern)
  • Action.Submit (common for bot + some automation patterns)

Actions are where host differences show up fastest, so baseline actions should be conservative.

Recommended baseline schema approach

  • Use a conservative version (commonly 1.2 or 1.3) and avoid newer features unless you know every host supports them.
  • Validate your JSON against the Adaptive Cards schema.
  • Test in each host early (Teams + Copilot Studio + your Flow action).

(We’ll keep templates aligned with this idea.)


Step 2 — Decide the “link strategy” that works in all hosts

You asked specifically for Adaptive Cards with links. Here’s the portable approach:

Primary navigation: use Action.OpenUrl

This is the most predictable cross-host way to open:

  • a SharePoint item
  • a Teams deep link
  • a DevOps work item
  • a report
  • a document

Action.OpenUrl is explicitly defined by the Adaptive Cards action model.

Secondary reference links: inline links in text (optional)

Some hosts support Markdown links in TextBlock, some sanitize more aggressively. If you need “always works,” keep important links as buttons.


Step 3 — Know the host-specific behavior (so you don’t get surprised)

A) Microsoft Teams (bots, message extensions)

Teams supports Adaptive Cards, but there are Teams-specific constraints and behavior around actions and supported features depending on the scenario (bot message vs compose extension vs task module, etc.).

Practical rules

  • Prefer one primary action + maybe one secondary
  • Keep payload sizes small (submit data, not huge objects)
  • Expect minor UI differences across desktop/mobile

B) Power Automate

Power Automate can post cards to Teams and wait for response. The most common pattern is:

  • Action.Submit returns a payload that Flow consumes

But: the precise behavior can vary by connector/action type and Teams context. The safest universal pattern is still: Action.OpenUrl for links + Action.Submit for decisions.

C) Copilot Studio

Copilot Studio supports Adaptive Cards but can have explicit schema and feature support boundaries. Always design with schema support in mind and test within Copilot Studio’s chat surface.

D) SharePoint / Viva Connections (SPFx)

In Viva Connections, the mainstream “cards” story is Adaptive Card Extensions (ACE) in SPFx, which is related but has its own constraints and lifecycle. Microsoft provides guidance and patterns for bot-powered ACE scenarios.


Step 4 — Use these 5 copy/paste templates (portable + host-aware)

Below are 5 templates you can adapt. They’re written as portable baseline cards with conservative elements and safe actions.

Replace placeholders like https://contoso... and IDs with your real values.


Template 1 — “Simple info + Open item link” (most portable)

{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body": [
{
"type": "TextBlock",
"text": "Daily Report",
"weight": "Bolder",
"size": "Medium",
"wrap": true
},
{
"type": "TextBlock",
"text": "A new item requires your attention.",
"wrap": true,
"spacing": "Small"
},
{
"type": "FactSet",
"facts": [
{ "title": "Project:", "value": "Example Project" },
{ "title": "Status:", "value": "Waiting for review" },
{ "title": "Created:", "value": "2026-02-27 10:15" }
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Open item",
"url": "https://contoso.sharepoint.com/sites/ExampleSite/Lists/ExampleList/DispForm.aspx?ID=123"
}
]
}

Why this works everywhere:

  • Simple body elements
  • Single Action.OpenUrl button (high portability)

Template 2 — “Approve / Reject” (Teams bot + Power Automate-friendly)

{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body": [
{
"type": "TextBlock",
"text": "Approval Request",
"weight": "Bolder",
"size": "Medium",
"wrap": true
},
{
"type": "TextBlock",
"text": "Please review the request below and choose an action.",
"wrap": true,
"spacing": "Small"
},
{
"type": "FactSet",
"facts": [
{ "title": "Request ID:", "value": "REQ-00491" },
{ "title": "Owner:", "value": "user@contoso.com" },
{ "title": "Impact:", "value": "Medium" }
]
},
{
"type": "Input.Text",
"id": "comment",
"isMultiline": true,
"placeholder": "Optional comment (recommended for rejection)"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Approve",
"data": { "action": "approve", "requestId": "REQ-00491" }
},
{
"type": "Action.Submit",
"title": "Reject",
"data": { "action": "reject", "requestId": "REQ-00491" }
},
{
"type": "Action.OpenUrl",
"title": "Open request details",
"url": "https://contoso.sharepoint.com/sites/ExampleSite/Lists/Requests/DispForm.aspx?ID=491"
}
]
}

Notes:

  • Action.Submit is your “workflow hook” (Flow/Bot reads data)
  • Keep the payload minimal and deterministic
  • The OpenUrl is the safe navigation fallback

Template 3 — “Search-like card with multiple links” (docs, dashboards, items)

{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body": [
{ "type": "TextBlock", "text": "Quick Links", "weight": "Bolder", "size": "Medium" },
{
"type": "TextBlock",
"text": "Choose a destination:",
"wrap": true,
"spacing": "Small"
}
],
"actions": [
{ "type": "Action.OpenUrl", "title": "Open SharePoint item", "url": "https://contoso.sharepoint.com/sites/ExampleSite/Lists/Example/DispForm.aspx?ID=123" },
{ "type": "Action.OpenUrl", "title": "Open Power BI report", "url": "https://app.powerbi.com/groups/me/reports/REPORT_ID" },
{ "type": "Action.OpenUrl", "title": "Open DevOps work item", "url": "https://dev.azure.com/contoso/Project/_workitems/edit/12345" },
{ "type": "Action.OpenUrl", "title": "Open documentation", "url": "https://learn.microsoft.com/" }
]
}

Why this is “all-host safe”:

  • Only Action.OpenUrl actions (least surprising behavior)

Template 4 — “Status update with ToggleVisibility” (use when supported)

Action.ToggleVisibility is a nice UX trick, but treat it as “optional” because not all hosts behave identically. The action is part of Adaptive Cards actions.

{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body": [
{ "type": "TextBlock", "text": "Job Status", "weight": "Bolder", "size": "Medium" },
{ "type": "TextBlock", "text": "Current state: Running", "wrap": true },
{
"type": "Container",
"id": "detailsBlock",
"isVisible": false,
"items": [
{ "type": "TextBlock", "text": "Last step: Copying list items", "wrap": true, "spacing": "Small" },
{ "type": "TextBlock", "text": "Job ID: 8b2f1d2c-xxxx-xxxx-xxxx-0f3a...", "wrap": true, "spacing": "Small" }
]
}
],
"actions": [
{
"type": "Action.ToggleVisibility",
"title": "Show/Hide details",
"targetElements": [ "detailsBlock" ]
},
{
"type": "Action.OpenUrl",
"title": "Open job logs",
"url": "https://contoso.sharepoint.com/sites/ExampleSite/Lists/Jobs/DispForm.aspx?ID=77"
}
]
}

Template 5 — “Copilot Studio / bot-friendly data capture” (minimal inputs + submit)

This is a classic “ask a few fields, submit them” design:

{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body": [
{ "type": "TextBlock", "text": "Create a request", "weight": "Bolder", "size": "Medium" },
{ "type": "Input.Text", "id": "title", "placeholder": "Title", "maxLength": 80 },
{
"type": "Input.ChoiceSet",
"id": "priority",
"style": "compact",
"choices": [
{ "title": "Low", "value": "Low" },
{ "title": "Medium", "value": "Medium" },
{ "title": "High", "value": "High" }
]
},
{ "type": "Input.Text", "id": "description", "isMultiline": true, "placeholder": "Description" }
],
"actions": [
{
"type": "Action.Submit",
"title": "Submit",
"data": { "scenario": "createRequest" }
}
]
}

Copilot Studio note:

  • Keep it simple and validate what the Copilot host supports (schema + elements).

Step 5 — Testing workflow (the “do it right” sequence)

To avoid the classic “works in designer, breaks in Teams/Flow/Copilot”:

  1. Validate JSON against Adaptive Cards schema (early).
  2. Test in Adaptive Cards Designer (visual sanity).
  3. Test in Teams (desktop + mobile) if Teams is a host.
  4. Test in Power Automate using the exact action you’ll ship.
  5. Test in Copilot Studio inside the real chat surface (schema constraints can differ).
  6. If using Viva/ACE, test in the Viva Connections context (ACE lifecycle and rendering constraints differ).

MS Learn links (the best “official” set to study)

  • Adaptive Cards overview:
  • Adaptive Cards actions reference:
  • Teams card actions (Teams behavior/constraints):
  • Teams cards reference (support notes):
  • Copilot Studio + Adaptive Cards overview/support:
  • SPFx Viva Connections bot-powered ACE tutorial:

Summary table (portable blueprint)

GoalRecommended patternWhy it works across “all”
Links that always workAction.OpenUrl buttonsMost portable action type
Workflow decisionsAction.Submit with minimal dataEasy for bots/flows to parse
Cross-host compatibilityConservative schema + common elementsHosts differ in feature support
Copilot scenariosKeep inputs/actions minimal; test in CopilotSchema constraints can apply
Viva/SharePoint cardsUse ACE patterns and test in VivaDifferent rendering lifecycle
Edvaldo Guimrães Filho Avatar

Published by