Teams Adaptive Card Universal Actions & Refresh Prompt
Design Adaptive Cards with Universal Actions (Action.Execute) and auto-refresh so a single shared card shows live, per-user state across a channel without reposting.
- Target user
- Developers building stateful, multi-user adaptive cards in Teams
- Difficulty
- Advanced
- Tools
- Claude, ChatGPT
The prompt
You are a senior Teams app developer who has built adaptive cards that stay correct for every viewer in a busy channel. I want to move off `Action.Submit` to Universal Actions (`Action.Execute`) with `refresh`, so one card reflects live state and shows the right buttons per user. I will provide: - The card's purpose (e.g. deploy gate, approval, incident status) - The states and per-role actions (who can approve, who can only view) - My bot/backend stack and where state lives - The clients I must support (desktop, mobile, web) and any fallback needs Your job: 1. **Why Universal Actions** — explain `Action.Execute` + `refresh` + `userIds` vs `Action.Submit`: the bot returns a fresh card, refresh auto-fires for listed users, and you avoid the "stale card / 50 reposts" problem. Note the host requirement and a graceful fallback for clients that don't support it. 2. **Refresh design** — define the `refresh` block (action + userIds list) and how the bot decides what card to return per user/role on invoke. Keep refresh cheap and idempotent; avoid refresh storms by capping the userIds list and debouncing. 3. **Per-user rendering** — server returns a card tailored to the invoking user's role and the current state (e.g. approver sees Approve/Reject; requester sees "awaiting approval"; everyone sees the audit trail). 4. **Invoke handler** — implement the `adaptiveCard/action` invoke: authenticate, authorize the action for that user, mutate state transactionally, and return either a new card (`application/vnd.microsoft.card.adaptive`) or an error/message response. 5. **Concurrency** — handle two users acting near-simultaneously (optimistic concurrency / version stamp) so the second click sees the resolved state, not a double-apply. 6. **Auth** — use SSO token exchange in the invoke so backend calls run as the user. Output as: (a) the adaptive card JSON with the refresh block, (b) the invoke handler returning per-user cards, (c) a state machine for the card, (d) a concurrency/versioning approach, (e) a fallback for unsupported clients. Bias toward: one living card over reposts, authorizing every Execute server-side, and idempotent refreshes that never storm.