Fix Adaptive Cards: Rendering, Schema & Integration Issues
Why This Is Happening
I've seen this exact situation play out dozens of times: a developer spends hours crafting a beautiful Adaptive Cards JSON payload, drops it into their Teams bot or Outlook actionable message , and nothing renders the way it should. The card looks great in the designer preview, but in production it's a broken mess, missing elements, or just a blank white box.
Here's the thing about Adaptive Cards that trips up even experienced developers: the technology is intentionally platform-agnostic. That's a feature, not a bug. But it means your JSON payload is transformed into native UI by the host application , Microsoft Teams, Outlook, Windows, or whatever surface you're targeting, and each host applies its own styling rules, schema version support, and rendering logic. When those don't line up with what you authored, things break silently.
The most common root causes I see are:
- Schema version mismatch, You authored the card against schema version 1.5 or 1.6, but the host app only supports up to 1.3. Elements introduced in newer schema versions simply get dropped without throwing an obvious error.
- Rendering SDK not installed or misconfigured, If you're building a custom host application and you haven't installed the correct platform-specific rendering SDK, Adaptive Cards won't render at all. The SDK is what bridges the JSON to native UI.
- Host configuration not set, Every host app has a Host Config JSON file that defines colors, fonts, spacing, and which elements are allowed. If yours is missing or malformed, the card either breaks visually or gets rejected entirely.
- Template data binding errors, The Adaptive Cards Templating system separates card layout from data. When the data binding expressions in your template reference properties that don't exist in the supplied data object, elements silently disappear.
- Actionable message registration issues in Outlook, Outlook has strict requirements around sender registration for actionable messages. An unregistered sender means your card renders as plain text fallback content.
I know this is frustrating, especially when the Adaptive Cards designer shows everything working perfectly and you can't figure out why production looks nothing like the preview. The gap between "it works in the designer" and "it works in my app" is exactly the problem this guide is here to solve.
The good news: Adaptive Cards are designed to be safe by default. Content is delivered in safe JSON payloads, which means there's no raw markup or script injection risk. The flip side is that the purely declarative model means you can't add custom code to paper over rendering gaps, you have to get the configuration right. Browse all Microsoft fix guides →
The Quick Fix, Try This First
Before you go deep on any of the steps below, run your card payload through the official Adaptive Cards Designer at adaptivecards.io/designer. This is the fastest way to identify whether your problem lives in the JSON itself or in how your specific host app is rendering it.
Open the designer, paste your JSON into the Card Payload Editor on the left, then switch the Select Host App dropdown to match the surface you're targeting, Microsoft Teams, Outlook Actionable Messages, Windows Notification, etc. Watch what changes. If the card breaks or drops elements when you switch host apps, your issue is a host-compatibility problem. If it looks wrong in every host, you have a JSON schema issue.
The single fix that resolves the majority of Adaptive Cards rendering problems is pinning your schema version correctly:
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3"
}
If you were using "version": "1.5" or higher and your host app doesn't support it yet, drop it back to "version": "1.3". Teams, Outlook, and most other host apps have solid support for 1.3. Anything above that depends on the specific app version your users are running. This one change alone fixes blank-card problems in roughly half the cases I've handled.
After you change the version, also check that you don't have any elements that were introduced in newer schema versions. Elements like Icon, Badge, and Carousel are newer additions and will silently not render on hosts that haven't added support for them yet. The designer will highlight unsupported elements with a warning icon when you select an older host app, that's your hit list.
Everything starts here. A malformed JSON payload causes silent failures that look identical to rendering SDK problems or host app bugs, so rule this out before anything else.
Open the Adaptive Cards Designer, paste your payload into the Card Payload Editor panel, and look at the bottom status bar. Any JSON syntax errors will show up immediately in red. Fix those first, a missing comma, an unclosed brace, or a misquoted string will stop the whole card from parsing.
Once the JSON is syntactically valid, look at the Warnings panel on the right side of the designer. This is where schema-level problems appear: unknown element types, invalid property values, missing required fields. Common ones I see are:
typeproperty missing from an element, every element in the card body needs an explicit type declaration- Using
style: "attention"on a container when the target host's schema version doesn't support that style value - Action types spelled incorrectly,
Action.SubmitnotAction.submit(it's case-sensitive)
For programmatic validation, use the adaptivecards npm package:
npm install adaptivecards
// In your validation script
const AdaptiveCards = require('adaptivecards');
const card = new AdaptiveCards.AdaptiveCard();
card.parse(yourCardPayload);
const validationResults = card.validateProperties();
validationResults.items.forEach(item => {
console.log(`[${item.phase}] ${item.message}`);
});
When this runs cleanly with zero validation items, you know your JSON is solid. You should see the card render correctly in the designer across all target host apps. If you still see issues after this, the problem is in the rendering SDK setup, move to Step 2.
If you're building a custom host application, your own website, desktop app, or mobile app, you need to install the platform-specific rendering SDK manually. This is what transforms your JSON into actual native UI. Without it, nothing renders. The SDK is not bundled with frameworks automatically.
Adaptive Cards officially supports these platforms with dedicated SDKs: Android, iOS, JavaScript, ASP.NET, .NET WPF, Windows, and ReactNative. Pick the one that matches your stack.
For JavaScript/Web apps:
npm install adaptivecards
Then in your app code:
import * as AdaptiveCards from 'adaptivecards';
const adaptiveCard = new AdaptiveCards.AdaptiveCard();
// Point to your host config
adaptiveCard.hostConfig = new AdaptiveCards.HostConfig({
fontFamily: "Segoe UI, Helvetica Neue, sans-serif"
});
adaptiveCard.parse(yourCardPayload);
const renderedCard = adaptiveCard.render();
// Attach to your DOM element
document.getElementById('card-container').appendChild(renderedCard);
For .NET / ASP.NET:
Install-Package AdaptiveCards -Version 2.7.3
For Android, add to your build.gradle:
implementation 'io.adaptivecards:adaptivecards-android:2.7.0'
One thing developers miss: after installing the SDK, you still need to supply a Host Config. If you skip the host config, the SDK uses internal defaults that may not match what you expect visually. The Host Config is a separate JSON object that controls the look and feel, colors, font sizes, element spacing, image sizes. The card author owns the content; the host app owns the styling. That's the core design contract of Adaptive Cards, and the Host Config is how you enforce it.
After installing and running, open your browser's developer console. The JavaScript SDK logs rendering warnings there, including elements it skipped due to host config restrictions. That's a fast way to spot what's silently getting dropped.
Teams is the most common surface where Adaptive Cards break in ways that make no sense at first glance. I've seen cards that work perfectly in the Bot Framework Emulator but arrive in Teams as plain text. Here's what's going on and how to fix it.
Teams Adaptive Cards are delivered through the Bot Framework. The Bot Framework wraps your Adaptive Card payload inside an attachment object. If that wrapper is malformed, Teams silently falls back to whatever text fallback you set (or nothing at all). Your card payload must be structured like this:
const card = {
type: "AdaptiveCard",
version: "1.3",
body: [ /* your elements */ ],
actions: [ /* your actions */ ]
};
const message = MessageFactory.attachment(
CardFactory.adaptiveCard(card)
);
The CardFactory.adaptiveCard() call is the part people miss, it correctly sets the contentType to application/vnd.microsoft.card.adaptive. If you manually build the attachment object and get that content type wrong, Teams won't know to render it as an Adaptive Card.
Second thing to check: Teams has its own schema version support ceiling, and it doesn't always match the latest Adaptive Cards schema release. As of early 2026, Teams Desktop supports up to schema 1.5 for most tenants, but older clients may cap at 1.3. Test with version: "1.3" first to establish a baseline that works everywhere, then selectively upgrade features that require newer schema versions.
Third: if your card has Action.Submit buttons and they're not responding, check that your bot has the teams channel enabled and that the serviceUrl in the incoming activity matches what Teams is sending. Mismatched service URLs cause submit actions to fail silently.
After applying these fixes, test the card in both the Teams web client and the Teams desktop app, they sometimes behave differently on schema version support, and you want to confirm both surfaces work before shipping.
Outlook Actionable Messages have additional requirements that go beyond just getting the JSON right. I've helped teams debug this for hours only to find the issue was a registration problem, not a code problem at all.
For Outlook to render your Adaptive Card as an actionable message rather than falling back to the plain HTML email body, your sending email address (or sending domain) must be registered with Microsoft's Actionable Email Developer Dashboard. This is mandatory for production. Without registration, Outlook silently ignores the card markup.
Go to outlook.office.com/connectors/oam/publish and register your sender. During development you can use self-service registration and test only to your own mailbox. For organization-wide or global deployment, you need admin approval.
Once registered, your email's <head> must include the correct script tag that embeds the Adaptive Card payload:
<script type="application/adaptivecard+json">
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"originator": "YOUR-ORIGINATOR-ID-FROM-REGISTRATION",
"body": [ ... ],
"actions": [ ... ]
}
</script>
The originator field is critical, it ties the card back to your registered application ID. Get this wrong and Outlook won't render the card. The value comes from your registration record in the Actionable Email Developer Dashboard.
Also: Outlook Actionable Messages support a more limited schema version than Teams. Stick to schema version 1.0 for maximum compatibility, especially if you're targeting both Outlook desktop and Outlook on the web. Elements like ColumnSet and FactSet work well; more advanced layout features from newer schema versions may not render correctly in all Outlook clients.
When testing, use a personal Outlook.com address first, the self-service registration process is faster and you can iterate without waiting for IT admin approval.
Adaptive Cards Templating is one of the most powerful features, it lets you separate your card layout from your data so you can reuse the same template with different datasets. But it's also where a lot of silent bugs hide.
The templating system uses data binding expressions wrapped in curly braces. When the bound property doesn't exist in your data, the element doesn't render an error, it just shows nothing. That's intentional behavior for safety, but it makes debugging genuinely painful.
Install the templating SDK alongside the rendering SDK:
npm install adaptivecards-templating
Then use it like this:
import { Template } from 'adaptivecards-templating';
const template = new Template(yourCardTemplate);
// This is where binding happens, pass your data object here
const cardPayload = template.expand({
$root: {
title: "Invoice #4821",
amount: "$1,240.00",
dueDate: "2026-05-01"
}
});
// Now render cardPayload as you normally would
adaptiveCard.parse(cardPayload);
To debug binding errors, add a console.log(cardPayload) after the template.expand() call and inspect the output JSON. Look for any text nodes that still contain raw binding expressions like ${title} instead of resolved values, those tell you exactly which data properties are missing or misnamed.
Common binding mistakes:
- Using
${item.name}when iterating over an array but the array items use a different property name - Forgetting that binding expressions are case-sensitive,
${Title}and${title}are different - Nesting data incorrectly under
$root, the entire data context needs to be under$rootin the expand call
The template service at templates.adaptivecards.io can automatically find a template for a given data shape and even pre-populate it. If you're building something data-driven and want to skip manual template authoring, this is worth exploring, you feed it your data schema and it returns a ready-to-use card template. That said, review whatever it returns before shipping to production; auto-generated templates sometimes need layout tweaks for your specific use case.
Advanced Troubleshooting
Debugging Rendering in Custom Host Applications
If you've built a custom host application using the Rendering SDK and cards still aren't behaving, the Host Config is almost always the culprit. The Host Config JSON defines every visual property the card is allowed to use. If your Host Config sets maxImageSize too small, images get clipped. If it doesn't include a color entry for attention style, those container styles silently fall back to the default.
Get the full default Host Config from the official GitHub repository at github.com/microsoft/AdaptiveCards under the rendering SDK for your platform. Start from that default and modify only the properties you actually need to change. Don't write a Host Config from scratch, there are too many required fields and the defaults are carefully calibrated.
Enterprise and Domain-Joined Scenarios
In enterprise environments, Adaptive Cards in Teams can be affected by Group Policy and Conditional Access policies. If users report that card actions (like Action.OpenUrl) are not working, IT administrators should check:
- Whether URL filtering policies are blocking the action URLs
- Whether the Teams app version deployed via MSI matches the schema version your cards target, enterprise deployments often lag behind consumer clients by several months
- Whether Data Loss Prevention (DLP) policies are stripping card payloads before delivery
Check the Teams Admin Center under Teams apps > Manage apps for any policy restrictions on bot interactions. If your bot is flagged by an app permission policy, its card payloads may be blocked at the tenant level even when the bot itself appears to be working.
Event Log and Network Analysis
For desktop rendering issues on Windows, open Event Viewer (run eventvwr.msc) and check Windows Logs > Application. Filter by source for your application name. Adaptive Cards rendering exceptions in .NET WPF applications show up here with full stack traces, much more useful than the generic "card failed to render" message you see in the UI.
For web-based Adaptive Cards in Teams or Outlook Web, open your browser's Developer Tools (F12), go to the Console tab, and filter for errors or warnings from the adaptivecards domain. The JavaScript SDK is verbose when it drops elements, you'll see exactly which element type it rejected and why.
On the network side, if your card's Action.Http calls aren't completing, check the Network tab in Dev Tools for the outbound request. CORS errors are a frequent cause, the target endpoint must allow cross-origin requests from the Teams or Outlook domain.
Designer SDK Integration Issues
The Designer SDK lets you embed the full Adaptive Cards designer UI inside your own application, useful if you want to give non-technical users a card-authoring experience without sending them to the public designer website. If the designer isn't rendering correctly inside your app, check that you've included both the CSS and JS bundles, and that your Content Security Policy headers allow inline styles (the designer needs them for the card preview pane).
Most Adaptive Cards issues are fixable by following the steps in this guide, but escalate to Microsoft Support if: (1) Your Outlook Actionable Messages registration is stuck in pending state for more than 5 business days, this requires Microsoft backend intervention. (2) Cards render correctly in the designer but Teams users in a specific tenant or region see blank cards, this can indicate a platform-side rendering service incident. (3) Your Bot Framework service is returning 200 responses but cards never appear in Teams, this sometimes indicates a service bus routing issue on Microsoft's infrastructure. In all these cases, include your card JSON payload, the schema version, target host app version, and any error messages from browser console or Event Viewer when you open a support ticket.
Prevention & Best Practices
The best way to avoid Adaptive Cards headaches is to build your development workflow around the tools and principles that the platform was designed around from day one. The schema is open-ended and extensible, but that extensibility cuts both ways. Without discipline in how you author and test cards, you end up chasing compatibility bugs across every update cycle.
First: always test against the oldest schema version your target hosts support, not the newest. The Adaptive Cards design philosophy is "when in doubt, keep it out", simpler cards are more compatible cards. If a layout goal can be achieved with a basic Container and TextBlock, don't reach for a newer element type just because it exists. Save newer schema features for cases where they provide genuine value that basic elements can't replicate.
Second: make the Adaptive Cards Designer part of your build pipeline. The Designer SDK exposes a full configuration API, which means you can run automated card validation in CI/CD. Before any card template ships, validate it programmatically against every target host app config you support. A card that fails validation in CI never reaches users.
Third: use the Adaptive Cards Templating system instead of string-interpolating data into your card JSON. Hand-building JSON strings with embedded data values is fragile and a potential injection risk. The templating system handles data binding safely and consistently. It also makes your cards reusable across different data sources, which saves engineering time as the product grows.
Fourth: for Teams bot scenarios, build your card with the fallback text property populated on your activity. When your card doesn't render, whether due to a schema version issue, a client that doesn't support cards at all, or a temporary service hiccup, the fallback text gives users actionable information rather than silence.
- Pin your card schema to the lowest version that covers all your required elements, don't target latest unless you specifically need new features
- Keep a Host Config file per target surface in your repository and update it whenever the platform publishes changes to their rendering defaults
- Run
adaptiveCard.validateProperties()in your unit tests, failing validation should be a CI gate, not a runtime surprise - Register your Outlook sender domain before you start building actionable message content, the approval process has a queue and you don't want it blocking your launch
Frequently Asked Questions
I'm building a Copilot or Teams bot, do Adaptive Cards work the same way across both?
Mostly yes, but with some important differences. Adaptive Cards in Microsoft Teams have been the standard for bot UX for years and have deep integration with the Bot Framework. For Copilot scenarios, the same Adaptive Cards schema applies, but Copilot has its own extensibility model through plugins and connectors that affects how and when cards are surfaced. The new Adaptive Card Documentation Hub (accessible from adaptivecards.io) has dedicated guidance for Copilot scenarios specifically, if you're building for Copilot, start there rather than relying on the older Teams Bot Framework docs, since the card rendering context is different.
My Adaptive Card looks perfect in the designer but shows up blank in Teams, what's wrong?
Nine times out of ten this is a schema version mismatch or a malformed attachment wrapper in your bot code. Drop your card version to "version": "1.3" and make sure you're using CardFactory.adaptiveCard() from the Bot Framework SDK to wrap your payload, that sets the correct MIME type that Teams expects. Also check your browser console or Teams developer tools for rendering warnings from the Adaptive Cards JavaScript SDK. If you see element names being logged as "unknown", those are the culprits getting dropped silently.
How do I safely let third-party developers send Adaptive Cards into my app without security risks?
This is one of the core design wins of Adaptive Cards, content is delivered as a safe, purely declarative JSON payload. There's no raw HTML markup or script execution allowed in the schema. The host app always controls the rendering through its Host Config, which means third-party card authors can never override your app's visual styling or inject executable code. To be safe, run adaptiveCard.validateProperties() on any incoming payload before rendering it, and consider capping the schema version you accept to one you've specifically tested against your host config.
Can I create and share reusable Adaptive Card templates for different data types?
Yes, Adaptive Cards Templating is specifically built for this. You author a card layout once, using data binding expressions like ${propertyName}, and then call template.expand({ $root: yourData }) at runtime to generate the final card payload. The template service at templates.adaptivecards.io goes further: you can submit your data schema and it will find or generate a matching template. Teams and SharePoint both support template-based cards in their extensibility models, and the templating SDK is available for all major platforms.
My Outlook Actionable Message card isn't rendering, users just see the plain email. What do I check first?
The most likely cause is that your sender address or domain isn't registered with Microsoft's Actionable Email Developer Dashboard. This step is mandatory, without it, Outlook treats your card markup as if it doesn't exist. Go to outlook.office.com/connectors/oam/publish and check your registration status. Once registered, make sure your email includes the <script type="application/adaptivecard+json"> block in the HTML head section with a valid originator ID that matches your registration. Also check that your card's schema version is set to "version": "1.0" for the broadest Outlook client compatibility.
How do I embed the Adaptive Cards designer inside my own web application?
Install the Designer SDK via npm: npm install adaptivecards-designer. The SDK exposes a full configuration API that lets you pre-load host app configurations, restrict available elements to a curated set, inject sample data for template previewing, and handle save/export events from the designer UI. Include both the CSS (adaptivecards-designer.css) and JS bundles, then initialize it against a container element in your DOM. Make sure your Content Security Policy allows inline styles since the card preview renderer needs them. The official docs at adaptivecards.io/designer have the complete configuration reference.