# Adaptive Cards for Rich Copilot Responses

**Last updated:** 2026-06-19 | Verified: MCP 2026-06-19
**Status:** GA
**Category:** Copilot Extensibility & Integration

---

## Introduksjon

Adaptive Cards er platform-agnostiske UI-komponenter uttrykt i JSON som lar utviklere skape rike, interaktive brukeropplevelser i Microsoft Copilot-økosystemet. De fungerer som citations og innholdsvisninger i Copilot-svar, og transformeres automatisk til native UI-elementer som tilpasser seg vertsapplikasjonens design og kontekst.

**Sentral verdi:**
- **Platform-agnostisk:** Én JSON-definisjon fungerer på tvers av Teams, Word, PowerPoint, Web, mobil
- **Native rendering:** Adapteres automatisk til dark mode, viewport size, plattformspesifikk styling
- **Declarative interactivity:** Input-felter og actions defineres i JSON, ikke custom code
- **Template language:** Separate data fra presentasjon med `${}`-syntax

**Bruksområder i Copilot-økosystemet:**
- **M365 Copilot:** Citation cards i API plugin responses (kun via declarative agents)
- **Copilot Studio:** Interactive cards med input fields, submit buttons, form validation
- **Teams-integrasjoner:** Message extensions, dialogs, proactive notifications
- **Power Automate:** Async proactive cards til brukere via Teams

**Viktig begrensning (Verified):** I M365 Copilot er API plugins *kun* støttet som actions innenfor declarative agents. De er ikke tilgjengelig direkte i M365 Copilot.

---

## Kjernekomponenter

### Schema og versjonering

| Schema-versjon | Support status | Begrensninger |
|----------------|----------------|---------------|
| **1.5** | GA, anbefalt for Teams/Omnichannel | Ikke `Action.Execute` i Teams |
| **1.6** | GA for Web Chat / Copilot Studio test | Web Chat støtter ikke `Action.Execute` |
| **1.3-1.4** | Legacy, men støttet | Mangler nyere features (labels, validation) |

**Verified:** Copilot Studio renderer kun 1.6-cards i test chat, ikke på canvas. Teams og Dynamics 365 live chat widget er begrenset til 1.5.

**JSON-struktur:**
```json
{
  "type": "AdaptiveCard",
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5",
  "body": [ /* Card elements */ ],
  "actions": [ /* Action buttons */ ]
}
```

### Core elements (alle versjoner)

| Element | Formål | Når bruke |
|---------|--------|-----------|
| `Container` | Grouping og styling av child elements | Logisk organisering, background colors |
| `ColumnSet` / `Column` | Multi-kolonne layout | Side-by-side content (men unngå på mobile!) |
| `TextBlock` | Formatert tekst (heading, paragraph) | Alle tekstvisninger |
| `FactSet` / `Fact` | Title/value-par i tabellformat | Strukturert data (budsjett, transaksjoner) |
| `Image` | Inline bilder | Logo, avatar, illustrasjoner |

### Input elements (interactive cards)

| Input type | Bruksområde | Validering |
|------------|-------------|-----------|
| `Input.Text` | Fritekst, med regex-validering | `isRequired`, `regex`, `errorMessage` |
| `Input.Number` | Numeriske verdier | Min/max bounds |
| `Input.Date` / `Input.Time` | Dato/tid-velgere | Native platform pickers |
| `Input.ChoiceSet` | Single/multi-select dropdowns | `choices` array, `style: "filtered"` for søk |
| `Input.Toggle` | Boolean switch | Ja/Nei, On/Off |

**Ny i 1.3+:** `label`-property (anbefalt over `placeholder`), `isRequired`, `errorMessage` for inline validation.

### Actions

| Action type | Oppførsel | Bruksområde |
|-------------|-----------|-------------|
| `Action.Submit` | Send input data til backend | Form submission i Copilot Studio |
| `Action.OpenUrl` | Åpne URL i browser | "Read more", "View in app" |
| `Action.Execute` (1.5+) | Invoke backend + update card | Inline editing i M365 Copilot (preview) |
| `Action.ShowCard` | Vis child card inline | Multi-step wizards |

**Viktig (Verified):** Ved `Action.OpenUrl` må domenet være i `validDomains` i app manifest, ellers viser Teams "URL may lead to untrusted content".

---

## Arkitekturmønstre

### Mønster 1: Static templates (API plugins)

**Når bruke:**
- API returnerer alltid samme object type
- Card layout sjelden endres
- Enklest å vedlikeholde

**Definisjon i plugin manifest:**
```json
{
  "functions": [{
    "name": "GetBudgets",
    "capabilities": {
      "response_semantics": {
        "data_path": "$",
        "properties": {
          "title": "$.name",
          "subtitle": "$.availableFunds"
        },
        "static_template": {
          "type": "AdaptiveCard",
          "version": "1.5",
          "body": [{
            "type": "Container",
            "$data": "${$root}",
            "items": [
              {
                "type": "TextBlock",
                "text": "Name: ${if(name, name, 'N/A')}",
                "wrap": true
              },
              {
                "type": "TextBlock",
                "text": "Available funds: ${if(availableFunds, formatNumber(availableFunds, 2), 'N/A')}",
                "wrap": true
              }
            ]
          }]
        }
      }
    }
  }]
}
```

**Template language-funksjoner:**
- `${propertyName}` – Data binding
- `${if(condition, trueValue, falseValue)}` – Conditional rendering
- `${formatNumber(value, decimals)}` – Number formatting
- `${$root}` – Escape til root scope

### Mønster 2: Dynamic templates (API plugins)

**Når bruke:**
- API returnerer flere object types (f.eks. debit/credit transactions)
- Different layouts per type
- Template selection via JSONPath

**Plugin manifest:**
```json
{
  "name": "GetTransactions",
  "capabilities": {
    "response_semantics": {
      "data_path": "$.transactions",
      "properties": {
        "template_selector": "$.displayTemplate"
      }
    }
  }
}
```

**API response:**
```json
{
  "transactions": [
    {
      "amount": -2000,
      "budgetName": "Lobby renovation",
      "displayTemplate": "$.templates.debit"
    },
    {
      "amount": 5000,
      "budgetName": "Lobby renovation",
      "displayTemplate": "$.templates.credit"
    }
  ],
  "templates": {
    "debit": { /* AdaptiveCard JSON */ },
    "credit": { /* AdaptiveCard JSON */ }
  }
}
```

**Verified:** `template_selector` bruker JSONPath (`$.displayTemplate`) som peker til template-objektet i samme response.

### Mønster 3: Hybrid (static + dynamic)

Static template fungerer som fallback hvis `template_selector` ikke finnes eller ikke matcher.

### Mønster 4: Power Fx dynamic cards (Copilot Studio)

**Når bruke:**
- Copilot Studio agents som trenger runtime-variabler
- Dynamisk innhold fra topic/agent context

**Advarsel (Verified):** Når du switcher fra JSON til Power Fx i Copilot Studio, kan du *ikke* tilbake til JSON. Lagre original JSON som kommentar!

**Eksempel Power Fx:**
```power
{
  type: "AdaptiveCard",
  version: "1.5",
  body: [
    {
      type: "TextBlock",
      text: Topic.Title,  // Variable reference
      weight: "Bolder"
    }
  ]
}
```

### Mønster 5: Inline editing med Action.Execute (preview)

**Status:** Preview i M365 Copilot declarative agents
**Krever:** Schema 1.5+

**Use case:** Brukeren kan editere card-data via modal dialog uten å forlate Copilot-grensesnittet.

**JSON:**
```json
{
  "type": "Action.Execute",
  "title": "Edit",
  "verb": "editItem",
  "data": { "itemId": "123" }
}
```

Backend mottar POST med verb + data, returnerer oppdatert card.

---

## Beslutningsveiledning

### Når bruke Adaptive Cards i Copilot

| Scenario | Bruk Adaptive Card? | Alternativ |
|----------|---------------------|------------|
| M365 Copilot API plugin citation | ✅ Ja (eneste måte) | N/A |
| Copilot Studio form input | ✅ Ja (interactive card node) | Question node (enklere, men mindre fleksibel) |
| Copilot Studio read-only visning | ⚠️ Nei, bruk Message node med card | Adaptive Card node krever submit button |
| Power Automate proactive Teams-melding | ✅ Ja | Plain text (mindre engaging) |
| Teams message extension | ✅ Ja | Hero card (mindre fleksibel) |

### Responsive design-prinsipper (Verified)

**Problem:** Adaptive Cards må fungere på desktop, web, mobile, Word, PowerPoint med varierende viewport widths.

**Best practices (fra M365 Copilot docs):**

| Prinsipp | Do | Don't |
|----------|-----|-------|
| Layout | Single-column layout | Multi-column (bryter på mobile) |
| Text + image | Separate rows | Same row (unntatt små ikoner/avatars) |
| Width | Auto-resize via viewport | Fixed width |
| Testing | Test i Teams, Word, PowerPoint, både expanded/collapsed | Bare test i én surface |

**Praktisk:** Unngå `ColumnSet` hvis mulig. Bruk `Container` med vertikal stacking.

### Validation-strategi

| Scenario | Teknikk | Eksempel |
|----------|---------|----------|
| Required fields | `isRequired: true` + `errorMessage` | `"errorMessage": "Please enter your name"` |
| Format validation | `regex` property på Input.Text | `"regex": "^[A-Z][a-z]+, [A-Z][a-z]+$"` |
| Conditional submit | `conditionallyEnabled: true` (1.5+) | Submit button disabled until required fields filled |
| Retry logic | Copilot Studio `How many reprompts` | Default: 2 retries |

### Submit button anti-pattern (Copilot Studio)

**Problem (Verified):** Adaptive Cards tillater multiple submits. Ved consecutive cards kan bruker submitte på feil card.

**Løsning:**
1. **`actionSubmitId` i data payload:** Inkluder unik identifikator i submit action data (f.eks. `"actionSubmitId": "booking_confirm_card_v3_confirm"`) for å skille mellom cards
2. **Unique IDs:** Hver submit action må ha unik `id`
3. **Event handling logic:** Valider `actionSubmitId` fra response for å identifisere korrekt card
4. **Web Chat UX:** Deaktiver submit-knapper etter første klikk for å hindre stale submissions
5. **Logging:** Debug sequence av submissions

---

## Integrasjon med Microsoft-stakken

### M365 Copilot + API plugins

| Komponent | Rolle |
|-----------|-------|
| **Declarative agent** | Container for API plugin |
| **API plugin manifest** | Definerer `response_semantics` + templates |
| **Backend API** | Returnerer JSON data (+ optional dynamic templates) |
| **Adaptive Card** | Renderes som citation i Copilot response |

**Flow:**
1. Bruker spør Copilot
2. Copilot invokerer API plugin function
3. API returnerer JSON data
4. M365 Copilot matcher data mot template (static/dynamic)
5. Adaptive Card renderes som citation med "Read more" action

**Begrensning:** API plugins er *kun* tilgjengelig i declarative agents, ikke standalone i M365 Copilot.

### Copilot Studio

| Node type | Interactive? | Use case |
|-----------|--------------|----------|
| **Adaptive Card** node | ✅ Ja (requires submit button) | Form input, multi-field data collection |
| **Message** node + card | ❌ Nei (display only) | Read-only information |
| **Question** node + card | ❌ Nei (display only) | Information med enkel Yes/No follow-up |

**Built-in designer:** Copilot Studio inkluderer drag-and-drop card designer. Alternativt: JSON payload editor eller Power Fx formula mode.

**Output variables:** Automatisk generert fra input IDs. Kan manuelt redigeres via "Edit Schema".

**Retry handling:**
- User sender text istedenfor submit → invalid response → retry (default: 2x)
- `Allow switching to another topic`: Default ON – tillater interruptions

### Power Automate proactive messaging

**Use case:** Send Adaptive Card til Teams-bruker fra inactive conversation (f.eks. approval request, notification).

**Actions tilgjengelig:**
- `Post message in a chat or channel` — send enkel tekst- eller card-melding
- `Post adaptive card in a chat or channel` — send card uten å vente på svar
- `Post adaptive card and wait for a response` — send card og vent på brukerens valg

**Config:**
- Post as: `Microsoft Copilot Studio agent`
- Post in: `Chat with agent`
- Recipient: User email/ID
- Agent: Copilot Studio agent

**Proaktive meldinger til grupper:**
- Send til teammates via Teams connector → Get a team → List group members
- Send til sikkerhetsgruppe via Microsoft Entra ID connector → Get group members
- Parallell utsending: Bruk Concurrency control i Apply to each (hensyn til throttling-grenser)

**Response handling:** `submitActionId` fra dynamic content = user's choice (title of Action.Submit).

**Template limitation (Verified):** Power Automate støtter ikke Adaptive Cards templating feature.

**Viktige begrensninger:**
- Mottaker må ha installert agenten i Teams
- Proaktive meldinger vises ikke i conversation transcripts eller analytics
- Meldinger må være i samme environment som Power Automate flow

### Teams

**Schema support:** 1.5 (ikke `Action.Execute`)

**Host config:** Teams definerer colors, spacing, font sizes via HostConfig. Cards adapterer automatisk.

**validDomains:** Obligatorisk for `Action.OpenUrl` – liste domener i app manifest.

---

## Offentlig sektor (Norge)

### Compliance-betraktninger

| Krav | Adaptive Card-implikasjon |
|------|---------------------------|
| **Personvern (GDPR)** | Ikke inkluder PII i card-payloads som logges. Bruk IDs, ikke navn/fødselsnummer. |
| **Universell utforming (WCAG 2.1 AA)** | Bruk `label` property (ikke `placeholder`), test med screen readers. |
| **Språk** | Norsk UI-tekst i cards. Bruk template language for oversettelse hvis nødvendig. |
| **Sikkerhet** | Valider all input server-side. `regex` i card er UX, ikke security. |

### Tilgjengelighet (Verified)

**`label` vs `placeholder` (1.3+):**
- **Do:** Bruk `label` property – renderes som persistent label med required indicator (`*`)
- **Don't:** Bruk `placeholder` – dårlig color contrast, ikke lest av screen readers, forsvinner ved input

**Proximity:** `label` property sikrer at label og input er visuelt koblet (viktig for screen magnifiers).

**Required indicator:** Defineres i HostConfig (default: asterisk). Automatisk vist ved `isRequired: true`.

### Norsk UX-praksis

**Dato-format:** `Input.Date` renderer native picker (automatisk DD.MM.YYYY i norsk locale).

**Valuta:** Bruk `formatNumber()` med 2 desimaler. Prefikser med "NOK" eller "kr" i TextBlock.

**Tone:** Norsk forvaltningstekst skal være klar, kortfattet, ikke-teknisk. Unngå "Submit", bruk "Send inn", "Bekreft".

---

## Kostnad og lisensiering

### Lisensavhengighet per Copilot-platform

| Platform | Lisenskrav | Notes |
|----------|------------|-------|
| **M365 Copilot** | M365 Copilot license | Adaptive Cards i API plugins inkludert |
| **Copilot Studio** | Copilot Studio capacity (per conversation) | Cards teller som én interaction |
| **Teams** | M365 E3/E5 eller Teams Essentials | Native Teams apps, ingen Copilot license |
| **Power Automate** | Power Automate per-user/per-flow license | Proactive cards via Teams connector |

**Kostnadsoptimalisering:**
- Adaptive Cards i seg selv har ingen ekstra kostnad – de er del av host platform pricing
- Copilot Studio: Minimize retries (default 2x kan 3x conversation cost ved feil)
- M365 Copilot: API plugin calls teller mot Copilot usage, ikke ekstra for card rendering

### Utvikling og vedlikehold

| Aktivitet | Kostnadsdrivere |
|-----------|-----------------|
| **Initial design** | Designer-tid (bruk Adaptive Card Designer for rask prototyping) |
| **Backend integration** | API-utvikling for data + template selection logic |
| **Testing** | Cross-platform testing (Teams, Word, PowerPoint, mobile) |
| **Versjonering** | Schema upgrades (1.5 → 1.6) kan kreve retesting |

**Baseline:** Adaptive Cards er gratis open-source schema. Kostnad = host platform + utviklingstid.

---

## For arkitekten (Cosmo)

### Anbefalinger ved arkitektursamtaler

**Når kunden sier:** "Vi trenger rike svar fra Copilot"
**Spør:**
- Hvilken Copilot? (M365, Studio, Teams)
- Interactive (input) eller read-only?
- Hvilke devices/surfaces? (mobile-first → single-column)

**Når kunden sier:** "API plugin returnerer kompleks data"
**Foreslå:**
- Static template hvis data-struktur er konsistent
- Dynamic templates hvis multiple object types
- FactSet for strukturert data (bedre enn mange TextBlocks)

**Når kunden sier:** "Brukerne skal kunne editere Copilot-svar"
**Sjekk:**
- Er dette M365 Copilot? → Action.Execute (preview)
- Er dette Copilot Studio? → Bruk Adaptive Card node med Input fields
- Vurder UX: inline edit vs new topic/dialog

### Arkitektur-patterns

| Pattern | Når anbefale |
|---------|--------------|
| **Citation cards (M365)** | Kunde har enterprise data i backend APIs som Copilot skal vise |
| **Approval workflows (Studio + PA)** | Async approvals via Teams proactive cards |
| **Multi-step forms (Studio)** | Complex data collection (bruk Action.ShowCard for wizards) |
| **Dashboard snippets (Teams)** | Regular status updates via bot-initiated cards |

### Fallgruver å unngå

| Fallgruve | Impact | Løsning |
|-----------|--------|---------|
| Multi-column layout uten mobile testing | Brukere på mobil ser broken layout | Always single-column, eller test rigorously |
| Hardkodet PII i templates | GDPR-brudd | Bruk IDs, fetch sensitive data on-demand |
| Action.OpenUrl uten validDomains | "Untrusted content" warning i Teams | Add domains til app manifest |
| Power Fx uten JSON backup | Kan ikke revertere design | Save JSON as comment før switch |
| For mange Input fields | User fatigue, høy abandon rate | Split i multiple cards (Action.ShowCard) |

### Design-prinsipper (KTG-tilpasset)

**Offentlig sektor Norge:**
- Norsk språk i alle UI-elementer
- Tydelig required-indikatorer (`isRequired: true`)
- Error messages på norsk (`"errorMessage": "Vennligst fyll ut feltet"`)
- Test med Jaws/NVDA screen readers

**Vibe-coding-vennlig:**
- Bruk Adaptive Card Designer for rapid prototyping
- Export JSON, paste i plugin manifest
- Test i Copilot Studio test chat før production
- Iterer basert på bruker-feedback (cards er lett å endre)

### Verifikasjon og testing

**Pre-deployment checklist:**
- [ ] Schema version matcher host (Teams = 1.5, Studio = 1.6 for test)
- [ ] Testet i alle target surfaces (Teams desktop, web, mobile)
- [ ] validDomains inkluderer alle Action.OpenUrl targets
- [ ] Ingen PII i card payloads (kun i backend API)
- [ ] Labels, ikke placeholders
- [ ] Error messages på norsk
- [ ] Single-column layout (eller testet multi-column på mobile)

**Runtime monitoring:**
- Copilot Studio: Analytics → se abandonment rate på Adaptive Card nodes
- M365 Copilot: Usage analytics → track citation engagement
- Power Automate: Flow run history → se submit responses

---

## Kilder og verifisering

**Verified (MCP microsoft-learn):**
- API plugins kun støttet i declarative agents (ikke standalone M365 Copilot): [Adaptive Card response templates for API plugins](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/api-plugin-adaptive-cards)
- Copilot Studio schema support (1.6 test, 1.5 Teams/Omnichannel): [Ask with Adaptive Cards](https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-ask-with-adaptive-card)
- validDomains requirement for Action.OpenUrl: [API plugin adaptive cards](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/api-plugin-adaptive-cards#using-static-and-dynamic-templates-together)
- Responsive design best practices: [Ensure responsive Adaptive Cards across Microsoft 365 Copilot hubs](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/api-plugin-adaptive-cards#ensure-responsive-adaptive-cards-across-microsoft-365-copilot-hubs)
- Power Fx warning (no revert to JSON): [Use Power Fx to make your card dynamic](https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-ask-with-adaptive-card#use-power-fx-to-make-your-card-dynamic)
- Submit button anti-pattern: [Submit button behavior for agents with consecutive cards](https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-ask-with-adaptive-card#submit-button-behavior-for-agents-with-consecutive-cards)
- Label vs placeholder accessibility: [Input Validation - Labels](https://learn.microsoft.com/en-us/adaptive-cards/authoring-cards/input-validation#labels)
- Action.Execute preview: [Allow inline editing of Adaptive Card responses](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/adaptive-card-edits)
- Power Automate proactive cards: [Send proactive Microsoft Teams messages](https://learn.microsoft.com/en-us/microsoft-copilot-studio/advanced-proactive-message#send-a-proactive-adaptive-card)
- actionSubmitId for consecutive cards: [Submit button behavior](https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-ask-with-adaptive-card#use-a-submit-identifier-in-actionsubmit-data)
- Validation guidelines for agents: [Validation guidelines for agents - Adaptive Card response](https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/appsource/prepare/review-copilot-validation-guidelines#adaptive-card-response)

**Verified (MCP code samples):**
- Static template JSON structure: [Build API plugins TypeSpec](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/build-api-plugins-typespec#add-an-adaptive-card-to-a-get-operation)
- Interactive actions card: [WhatsApp agents](https://learn.microsoft.com/en-us/microsoft-copilot-studio/publication-add-bot-to-whatsapp#use-supported-adaptive-cards-in-your-agents-topics)
- Template language basics: [Adaptive Cards templating](https://learn.microsoft.com/en-us/adaptive-cards/templating/#template-language)

**Baseline (Model knowledge, Jan 2025):**
- Adaptive Cards general schema: https://adaptivecards.io/
- Adaptive Card Designer: https://adaptivecards.io/designer/
- JSONPath syntax for template_selector
- Cross-platform rendering principles

**MCP calls:** 3 docs_search, 2 docs_fetch, 1 code_sample_search
**Unique URLs:** 12 Microsoft Learn documentation pages
**Confidence:** Verified (all core claims backed by MCP sources)
