Adaptive Card Responses in Microsoft 365 Copilot - Making API Plugins Actually Usable
Here is a thing that surprises people the first time they wire an API plugin into Microsoft 365 Copilot. You connect your internal system, you ask Copilot a question, and it answers with a wall of text. The data is correct. It is also nearly useless. Nobody wants to read a paragraph that says "Order 48821 has a status of Dispatched, an expected delivery of 2026-07-03, a carrier of StarTrack, and a tracking reference of TX99..." when what they actually wanted was a tidy little card with a status badge and a button to track the parcel.
That gap, between Copilot having the right answer and Copilot presenting it in a way a busy person can act on, is exactly what Adaptive Cards are for. And for any Australian business building real Copilot extensions rather than demos, getting this part right is the difference between staff using the thing and staff quietly going back to the old system.
We have built a fair number of these now, and I want to talk through what Adaptive Card responses are, where they shine, and the bits that are still rough enough to catch you out.
What an Adaptive Card response actually is
When you build an API plugin for Microsoft 365 Copilot, you describe your API to Copilot through an OpenAPI spec and a plugin manifest. Copilot reads a user's request, works out which operation to call, calls it, and gets back a JSON response from your API. By default it summarises that JSON in plain language.
An Adaptive Card response changes that last step. Instead of letting Copilot narrate the JSON, you give it a card template. The template is a JSON document that describes a layout: headings, fields, images, buttons, the lot. You bind the values from your API response into that template, and Copilot renders a proper visual card inside the chat instead of a paragraph.
If you have ever built anything in Teams, this will feel familiar, because it is the same Adaptive Card format. That reuse is genuinely handy. A card you designed for a Teams message bot is most of the way to being a card for a Copilot plugin.
The mechanics are not complicated. In your plugin manifest, each function can point at a response rendering template. The template uses binding expressions like ${status} or ${order.deliveryDate} to pull values out of the API response. When the function runs, Copilot merges the data into the template and shows the result. You can preview cards while you build them using the Adaptive Card designer, which I would strongly recommend doing rather than guessing.
This is the kind of integration work that sits right in the middle of what we do, and if you are scoping a project around it our Copilot Studio consulting team can tell you quickly whether your scenario is a good fit for cards or not.
Why this matters more than it looks
It is easy to dismiss the card layer as polish. It is not.
The first reason is trust. When Copilot summarises raw JSON into prose, it is doing a small amount of interpretation, and occasionally that interpretation drifts. A status field that says PARTIAL might get described as "partially complete" in one answer and "incomplete" in another. With an Adaptive Card you are showing the actual field value, styled, with no model in the middle reinterpreting it. For anything where the exact value matters, like an account balance, a compliance status, or a stock level, that determinism is worth a lot.
The second reason is action. A paragraph of text cannot do anything. A card can carry an Action.OpenUrl button that deep links straight into your line of business system, or an Action.Submit that posts back to your API. So instead of Copilot telling someone "this leave request is pending approval," a manager can see the request and approve it from inside the card without leaving the chat. That is the moment Copilot stops being a search box and starts being a place where work happens.
We saw this land well with a logistics client. Their dispatch team was asking Copilot about consignments dozens of times a day. The text answers were fine but slow to scan. We swapped to a card with a colour coded status, the three fields they actually cared about, and a track button. Same data, same API, but the time to get an answer and act on it dropped noticeably, and adoption went up because the thing finally felt built for them rather than bolted on.
What works well
A few things genuinely impressed me.
The reuse of the Adaptive Card schema across Teams and Copilot is real and it saves time. If your organisation already has a design language for cards, you are not starting from scratch.
The binding model is clean enough. Once you understand that the template is just JSON with placeholders, you can get a decent card working in an afternoon. Conditional layout using $when is useful for hiding fields that are empty, so you do not end up with a card showing "Notes: " followed by nothing.
And the fallback behaviour is sensible. If a card cannot render for whatever reason, Copilot falls back to a text response rather than failing outright. That matters in the real world, where you do not want a styling problem to break someone's whole query.
What is still rough
Now the honest part, because this is not all smooth.
Cards in Copilot are more constrained than cards in Teams. Not every Adaptive Card feature renders the same way, and some elements you might rely on in a Teams bot behave differently or get stripped. You have to test in the actual Copilot surface, not just the designer, because the designer will happily show you something that does not survive the trip into chat. We have been bitten by this more than once, building a beautiful card that looked wrong the moment it rendered for real.
Interactive actions are the area to watch most closely. Buttons that open a URL are reliable. Buttons that submit data back are more involved, because you have to think about authentication, about what happens to the response, and about the fact that the user is acting inside a Copilot conversation rather than your own app. Treat any write action as a proper piece of engineering, not a checkbox on the card. Plan the auth, plan the error states, plan what the user sees when the submit fails.
There is also a discipline problem that is nothing to do with Microsoft. Teams get excited and try to cram everything into the card. A card with fourteen fields and four buttons is worse than the paragraph it replaced. The skill is deciding what the person actually needs to see in that moment and ruthlessly cutting the rest. That is a design decision, not a technical one, and it is the part people underinvest in.
One more thing. Card design and prompt design are linked in a way that is easy to miss. Copilot still has to decide when to call your function and how to phrase the bit of text it puts above the card. If your function descriptions in the manifest are vague, Copilot will call the wrong thing or not call it at all, and the prettiest card in the world will not help. So you cannot treat the card layer in isolation. The manifest, the descriptions, and the card all have to work together, which is why we tend to build and test them as one piece.
How we approach it on a real project
When we scope a Copilot extension that uses cards, the order we work in is fairly fixed.
First we nail down the handful of questions people will actually ask. Not the hundred theoretical ones, the five or six that matter. Then we design the card for each of those around the smallest set of fields that lets someone make a decision. Then, and only then, do we wire the API and the manifest, because the card design tells us what the API actually needs to return. Plenty of times this surfaces that the existing API gives back too much or too little, and it is far cheaper to find that out before you have built the integration.
We test relentlessly in the real Copilot surface across desktop and mobile, because cards reflow and a layout that is fine on a wide screen can break on a phone. And we keep the write actions until last, because they carry the most risk and the most authentication complexity.
If you want a sense of how this fits into a broader piece of work, our Microsoft AI consulting practice handles the full path from Copilot strategy through to shipped extensions, and our AI agent builders team does the deeper custom work when a plain plugin is not enough.
Is it worth doing
For most internal Copilot extensions, yes. The moment your plugin returns anything structured, like a record, a status, a list of items people scan, a card pays for itself in clarity. The cases where I would not bother are when the answer is genuinely conversational, a summary or an explanation, where prose is the right format and a card would just get in the way.
The trap is treating Adaptive Cards as decoration. They are not decoration. They are the interface your staff actually touch, and a Copilot extension lives or dies on whether that interface respects their time. Build the card around the decision the person is trying to make, keep it lean, test it where it really runs, and you end up with something people reach for instead of tolerate.
If you are weighing up whether to build Copilot extensions at all, or you have one that technically works but nobody loves, that is a conversation worth having. Get in touch and we can look at it properly.
Reference: Adaptive Card responses, Microsoft 365 Copilot extensibility documentation