Learn how to design Adaptive Cards, post them to Teams channels, and collect structured responses using Power Automate's "post and wait" pattern. Build real interactive approval and status update flows — not just passive notifications.

Picture this: your team gets an email saying "please approve the Q3 budget proposal." The email has no context, no numbers, and the approver has to dig through three SharePoint folders and two Outlook chains before they can make a decision. They forget. The deadline passes. Nothing gets approved. This is the daily reality of notification-based workflows that rely purely on passive messages — emails and plain channel posts that require the reader to go somewhere else to take action.
Adaptive Cards change that dynamic entirely. An Adaptive Card is a structured, interactive message that appears directly inside a Teams channel or chat. Instead of a wall of text, your team member sees a formatted card with the budget figures right there, a text box for their comment, and Approve/Reject buttons — all without leaving Teams. Power Automate can both send those cards and wait for the response, meaning you can build end-to-end interactive workflows where a single Teams message is the entire interface for a business decision.
By the end of this lesson, you'll be able to design Adaptive Cards using the Adaptive Card Designer, inject dynamic data from your flow into them, post them to Teams channels, collect user responses, and route the workflow based on what respondents chose. We'll build a realistic team status update approval flow together, and you'll leave with a working mental model of how the "send and wait" pattern works.
What you'll learn:
Before diving in, you should be comfortable with the Power Automate designer interface. If you haven't built a flow before, start with Your First Power Automate Flow: Automated Email Notifications That Actually Work and come back here. You should also understand how triggers work — specifically, what kicks a flow off — which is covered in Power Automate Triggers: When to Start a Flow.
You'll need:
An Adaptive Card is a JSON-defined UI component. JSON (JavaScript Object Notation) is just a structured text format for describing data and layout — think of it like a recipe card where the ingredients and instructions are spelled out in a very specific format. When Teams receives that JSON, it renders it as a visual card with formatted text, images, tables, dropdown menus, text boxes, and buttons.
Here's what makes Adaptive Cards powerful compared to a plain Teams channel message:
Plain channel post: "Please review the marketing budget and reply with your decision."
Adaptive Card: A card titled "Marketing Budget Review — Q3 2025" showing a table with line items (Creative: $45,000 / Events: $30,000 / Digital: $25,000), a "Comments" text field, and two buttons: "Approve" and "Request Changes."
The card is self-contained. The decision-maker has everything they need right in front of them, and their response is captured as structured data — not a freeform reply that you'd have to parse manually.
Key insight: The fundamental shift with Adaptive Cards is moving from passive notifications (messages that say something happened) to active interfaces (messages that let someone do something). This is what separates a workflow from a notification system.
Power Automate's Teams connector offers several card-related actions. For this lesson, the two you need to understand are:
"Post an Adaptive Card to a Teams channel and wait for a response" — This action posts the card and then pauses the flow. The flow literally suspends itself and waits until someone interacts with the card and submits it. You set a timeout period (how long to wait), and the flow resumes when it gets a response or when time runs out.
"Post a card in a chat or channel" — This action posts a card but doesn't wait. It's for purely informational cards — status updates, summaries, notifications where you don't need input back.
For approvals and status updates where you need a decision, you'll use the first action. The "wait" capability is what makes the interactive pattern work — the flow doesn't race ahead to the "if approved" branch before anyone has actually approved anything.
Note: The "wait for a response" action uses a flow-internal timeout, not a hard server limit. You can configure it to wait minutes, hours, or up to 30 days. The flow shows as "running" in your run history during this wait period, which is normal.
Before you touch Power Automate, you need a card design. Microsoft provides a free, browser-based tool at adaptivecards.io/designer — navigate there in any browser. You'll see a split view: a visual preview on the left and the JSON editor on the right.
Let's design a card for our scenario: a department head submits a budget request, and the card goes to the finance team's channel asking for approval.
In the Adaptive Card Designer, start with a blank card. Use the toolbar to add elements:
Add a TextBlock — Set the text to Q3 Budget Approval Request and the size to Large, weight to Bolder. This is your card title.
Add another TextBlock — Set text to Submitted by: {{submitterName}} — the double curly braces are placeholder tokens you'll replace with dynamic data from your flow.
Add a FactSet — A FactSet renders key-value pairs in a clean table format. Add three facts:
Department, Value: {{department}}Total Amount, Value: {{totalAmount}}Submission Date, Value: {{submissionDate}}Add a TextBlock — Set text to Project Description: and weight to Bolder.
Add another TextBlock — Set text to {{projectDescription}} and enable text wrapping.
Add an Input.Text — This is where the reviewer types their comments. Set the ID to reviewerComments, the placeholder to Enter your review notes here..., and enable multiline.
Add an ActionSet — This holds your buttons. Add two Action.Submit actions:
Approve, ID: approve, style: positive (renders green)Request Changes, ID: requestChanges, style: destructive (renders red)When you click either button, Teams submits the entire card's input data back to Power Automate — including which button was clicked and whatever the reviewer typed in the comment box.
Tip: The ID you assign to each input element (like
reviewerComments) is how Power Automate identifies that field in the response data. Choose IDs that are descriptive and use camelCase — you'll be referencing them later as JSON property names.
Once your card looks right in the preview panel, copy the entire JSON from the right-hand editor. It will look something like this (simplified):
{
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "Q3 Budget Approval Request",
"size": "Large",
"weight": "Bolder"
},
{
"type": "TextBlock",
"text": "Submitted by: {{submitterName}}"
},
{
"type": "FactSet",
"facts": [
{ "title": "Department", "value": "{{department}}" },
{ "title": "Total Amount", "value": "{{totalAmount}}" },
{ "title": "Submission Date", "value": "{{submissionDate}}" }
]
},
{
"type": "TextBlock",
"text": "Project Description:",
"weight": "Bolder"
},
{
"type": "TextBlock",
"text": "{{projectDescription}}",
"wrap": true
},
{
"type": "Input.Text",
"id": "reviewerComments",
"placeholder": "Enter your review notes here...",
"isMultiline": true
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Approve",
"id": "approve",
"style": "positive"
},
{
"type": "Action.Submit",
"title": "Request Changes",
"id": "requestChanges",
"style": "destructive"
}
]
}
Keep this JSON handy. You're about to paste it into Power Automate.
Open Power Automate at make.powerautomate.com and create a new Instant Cloud Flow (we'll trigger it manually for testing). In production, you might trigger this from a SharePoint list item being created — if you're interested in that pattern, Power Automate with SharePoint: Automate Document Approvals covers the SharePoint trigger side in detail.
After the trigger, add "Initialize variable" actions for each piece of data you want to inject into the card. For our scenario:
submitterName, Type: String, Value: Priya Sharma (in production, this comes from the trigger)department, Type: String, Value: MarketingtotalAmount, Type: String, Value: $100,000projectDescription, Type: String, Value: Annual brand refresh campaign including digital assets, event presence, and creative production.submissionDate, Type: String, Value: Use the utcNow() expression formatted with formatDateTime(utcNow(), 'MMMM dd, yyyy')For a deeper look at how variables work in Power Automate flows, see Working with Conditions, Loops, and Variables in Power Automate.
Click New Step and search for "Post an Adaptive Card to a Teams channel and wait for a response." Select it from the Microsoft Teams connector.
Fill in the fields:
Now comes the critical part: replacing those {{placeholder}} tokens with actual dynamic values. In the Message field, paste your JSON, then find each placeholder and replace it with the corresponding variable from the dynamic content panel. For example, {{submitterName}} becomes the submitterName variable you initialized in Step 1.
This request has been reviewed. Thank you. — this text replaces the card after someone responds, so the channel doesn't show a stale interactive card to other members.YesPT4H means 4 hours, P1D means 1 day, P7D means 7 days.Warning: The "Should update card" setting matters more than it seems. If you leave it set to No, the card remains interactive after someone responds — meaning a second person could also click "Approve" and submit a duplicate response, potentially triggering your flow logic twice. Always set this to Yes for decision flows.
After the Teams action, Power Automate resumes the flow and makes the response available as dynamic content. The key output you'll work with is body/data, which is a JSON object containing all submitted values.
To extract the reviewer's comments, add a "Compose" action and use this expression:
outputs('Post_an_Adaptive_Card_to_a_Teams_channel_and_wait_for_a_response')?['body/data/reviewerComments']
To find out which button was clicked, you use:
outputs('Post_an_Adaptive_Card_to_a_Teams_channel_and_wait_for_a_response')?['body/submitActionId']
The submitActionId returns the ID of the button that was pressed — either approve or requestChanges, matching exactly the IDs you set in the Adaptive Card Designer.
Add a "Condition" action after your Compose steps. Set the condition to:
submitActionId is equal to approve
In the Yes branch, add whatever happens when approved — update a SharePoint list, send a confirmation email, post a "Approved ✅" message to another channel.
In the No branch, handle the "Request Changes" path — notify the submitter, create a task in Planner, whatever your process requires.
This kind of conditional branching — where the entire flow forks based on a value — is covered thoroughly in Working with Conditions, Loops, and Variables in Power Automate. If you're building a more complex multi-approver scenario, Building Approval Workflows with Power Automate walks through patterns for sequential and parallel approvals.
What happens when nobody responds within your timeout window? The flow doesn't fail — it moves on, but the response data will be empty. You need to detect this and handle it gracefully.
After the Teams action, add a "Condition" before your main branch:
Check whether body/submitActionId is equal to TimedOut
Power Automate returns the string "TimedOut" as the submit action ID when nobody responded within the timeout period. In the Yes branch, send an escalation — maybe an email to the manager, or a direct message to the original requestor saying the request needs resubmission.
In the No branch, continue with your normal approval/rejection logic.
Tip: For critical business processes, combine the timeout handler with a scheduled reminder. Build a separate scheduled flow that runs every 24 hours, queries your SharePoint list for open requests older than 48 hours, and sends a reminder message. This two-flow approach keeps your approval flow clean while ensuring nothing falls through the cracks.
Not every card needs a response. If you want to post a status update — say, a daily summary of open tickets formatted as a card — use the "Post a card in a chat or channel" action instead.
This action takes a card JSON in the same format, posts it to the channel, and the flow continues immediately. There's no waiting, no response collection. This is perfect for:
For scheduling flows like this to run at regular intervals, Scheduling and Managing Time-Based Flows in Power Automate: Recurrence Triggers, Time Zones, and Business Hours Logic is the right next read.
Build the following flow from scratch:
Scenario: Your team runs a weekly "project status check" where each project manager reports whether their project is On Track, At Risk, or Blocked. Instead of asking everyone to fill out a form or reply to an email, you want a card posted to your Teams channel every Monday morning that lets project managers click a status button and optionally add a note.
Your card should include:
statusNote for free-form commentsYour flow should:
submitActionId and statusNote valuesTest by running the flow manually (use the manual trigger option for testing rather than waiting until Monday). After the card posts, click one of the buttons in Teams, submit a comment, and verify the flow takes the correct branch.
The card JSON fails to render and the action errors out. This almost always means invalid JSON. Even a single missing comma or unclosed bracket breaks it. Go back to adaptivecards.io/designer, paste your JSON into the editor there, and check whether the preview renders correctly. The designer will highlight syntax errors.
The response body is empty after a submission.
Check that your input element IDs in the JSON exactly match how you're referencing them in the expression. reviewerComments ≠ ReviewerComments. JSON property names are case-sensitive.
The card stays interactive after someone responds. You left "Should update card" set to No, or the Update message field is blank. Set both correctly — a card without an update message will revert to showing nothing, which is confusing.
The flow errors on the Teams action saying "Insufficient permissions." Your Power Automate connection to Teams needs permission to post in the specific channel. If the channel is private, the account used by the connection must be a member of that private channel. Check your connection settings under Data → Connections in Power Automate.
The submitActionId is coming back as TimedOut immediately.
Your timeout value is incorrectly formatted. PT4H is correct for 4 hours. 4H alone will not parse correctly and may default to an instant timeout. Use ISO 8601 duration format precisely.
Warning: Be careful with flows that contain "wait for response" actions and high volumes. Each waiting flow instance consumes a flow run and counts against your API call limits. If you're sending cards to 500 people simultaneously, check your licensing and throttling limits. The Understanding Power Automate Licensing article covers what counts against which limits.
The card posts but the dynamic values appear as literal {{submitterName}} text.
You forgot to replace the placeholder tokens with actual dynamic content from the flow. In the Teams action's Message field, each placeholder must be replaced with a variable or dynamic content expression — not left as literal text.
If you run into issues that are harder to diagnose, the run history is your first stop. Using Power Automate Run History and Flow Checker to Debug and Fix Failing Flows walks you through reading action inputs and outputs to find exactly where things went wrong.
Adaptive Cards transform Teams channels from passive notification feeds into interactive workflow interfaces. Here's what you built understanding of today:
{{tokens}} in your JSON and swapping them for dynamic flow variablesbody/data/[inputId] and body/submitActionId from the action's outputsubmitActionId equals "TimedOut"From here, there are several natural directions to go deeper. If you're building multi-person approval workflows where cards need to go to several approvers in sequence or in parallel, Building Approval Workflows with Power Automate is the right next step. For more sophisticated card behaviors — like cards that update themselves with new data after submission, or cards built entirely from dynamic data structures — Implementing Adaptive Card-Based Human-in-the-Loop Approvals in Power Automate: Dynamic Forms, Contextual Data Injection, and Response Handling goes much deeper on the technical side.
You now have the foundation to make Teams channels genuinely interactive rather than just informational. That's a meaningful upgrade to any team's workflow.