Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Microsoft Teams By James Joyner IV · · 10 min read

Capturing Adaptive Card Responses in Teams Workflows Without a Bot

Build a Power Automate Workflow that posts an Adaptive Card and waits for a response — a no-code approval loop with responder authorization, timeouts, and card updates.

  • #microsoft-teams
  • #ai
  • #workflows
  • #power-automate
  • #approvals

When the old Office 365 connectors started winding down, a lot of teams scrambled to replace their incoming-webhook notifications with Teams Workflows. Most of those migrations stopped at parity: an alert fires, a card lands in a channel, done. But Workflows can do something the old connectors never could — it can wait for a response. That one capability lets you build a genuine approval loop, where a card asks a question and the flow branches on who clicked what, all without standing up and babysitting a Bot Framework bot. This post walks through that loop, including the authorization gap that most no-code approval flows leave wide open.

Post-and-wait, not fire-and-forget

The key action is “Post adaptive card and wait for a response.” Unlike the plain “Post card” action, this one pauses the flow until someone interacts with the card, then hands you back the responder’s identity and the submitted data. That pause is what turns a notification into a decision point.

A minimal flow shape:

  1. Trigger — an alert webhook, a deploy request, an access request.
  2. Post adaptive card and wait for a response — to the target channel or chat.
  3. Condition / Switch — branch on the submitted action value.
  4. Downstream action — approve the deploy, grant the access, or deny.

The card carries the actions and any input fields:

{
  "type": "AdaptiveCard",
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.4",
  "body": [
    { "type": "TextBlock", "text": "Approve deploy of api-gateway v2.9.1?", "wrap": true },
    { "type": "Input.Text", "id": "reason", "placeholder": "Reason (required for deny)" }
  ],
  "actions": [
    { "type": "Action.Submit", "title": "Approve", "data": { "decision": "approve" } },
    { "type": "Action.Submit", "title": "Reject",  "data": { "decision": "reject" } }
  ]
}

When someone clicks, the flow resumes and you read decision and reason from the response, along with the responder’s identity, and route accordingly.

The authorization gap nobody mentions

Here’s the problem that sinks a surprising number of no-code approval flows: the card cannot enforce who is allowed to approve. You posted it to a channel, and anyone in that channel can click “Approve.” The Adaptive Card schema has no concept of “only these users may submit this action.” If your flow trusts the click blindly, then the intern who wandered into the deploys channel can approve a production release.

The enforcement has to live in the flow. After the response comes back, check that the responder is on your authorized list before acting on their decision:

[Post card and wait for response]
        |
[Compose: responder = response.user.id]
        |
[Condition: responder is in AuthorizedApprovers?]
   |-- No  --> [Update card: "You're not authorized to approve this"]
   |           [Optionally re-post and keep waiting, or deny]
   |-- Yes --> [Switch on decision: approve / reject]

You can source AuthorizedApprovers from an Entra group, a SharePoint list, or a variable in the flow. The point is that the authorization decision is made after the click, inside the flow, where you actually control it — not by the card, which can’t. This is the single most important thing to get right, and it’s exactly the check most copy-pasted approval flows omit.

Timeout and escalation, so the flow doesn’t hang forever

A “wait for response” action without a timeout is a flow waiting for Godot. If nobody clicks, it sits there indefinitely, and whatever depended on the approval is blocked silently. Set an explicit timeout on the wait, and define what happens when it fires:

  • Re-post the card and ping a backup approver.
  • Escalate to a manager or an on-call group.
  • Auto-deny if your policy says no answer means no.

Pick the behavior that matches the risk. For a production deploy, auto-deny on timeout is safe; for a low-risk access request, re-pinging might be friendlier. Either way, decide — an unbounded wait is a latent incident.

Update the card after a decision

Once someone has decided, update the original message so it no longer shows live buttons. Otherwise a second person can click “Approve” on an already-approved request, and now your flow either double-processes or gets a confusing late response. Replacing the buttons with a “Approved by Bob at 14:32” text block both records the outcome and prevents the stale second click.

Drafting the flow with AI

The branching logic and the card JSON are well-suited to an AI draft, and I lean on that — while making sure the authorization check survives into the final version, because that’s the part the model will happily leave out if you don’t ask for it explicitly:

Prompt: “Design a Power Automate Workflow that posts an Adaptive Card with Approve/Reject and a reason field, waits for the response, checks the responder is in an authorized-approvers group before acting, branches on the decision, sets a timeout with an escalation path, and updates the card after a decision so it can’t be clicked again.”

Output (excerpt): A flow with the post-and-wait action, a Compose step extracting the responder id, a condition checking group membership before the decision switch, a configured wait timeout with a re-post escalation branch, and an update-message step replacing the actions with an outcome text block.

The model produces a complete flow skeleton. What I verify is that the responder-authorization condition is genuinely there and runs before the downstream action — because that’s the security control, and a flow that branches on the decision without first checking the responder is a flow that lets anyone approve anything.

What you end up with

A Workflows approval loop that posts a card, waits for a response, authorizes the responder, times out gracefully, and updates the card to a single resolution — all in the low-code surface, no bot to deploy or maintain. For the broader migration off the retiring connectors and the other Workflows patterns, the Microsoft Teams category has the related guides, and the prompts library includes a card-response Workflows prompt that bakes in the authorization and timeout handling so you don’t ship the version that lets the whole channel approve your deploys.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.