MS Teams Channel
Norq compiles Teams templates to Adaptive Card v1.5 JSON. Adaptive Cards (Microsoft’s rich message format for Teams) are the standard format for rich messages in Microsoft Teams.
Pipeline: Markdown -> AST (parse tree) -> Adaptive Card JSON
Template files
msteams.md– Markdown modemsteams.json– Native JSON mode (for inputs, toggles, custom layouts)
Frontmatter
---
enabled: true
---| Field | Required | Description |
|---|---|---|
enabled |
No | true (default) or false. |
Directive compilation
| Directive | Teams output |
|---|---|
:::header |
TextBlock with ExtraLarge size + Bolder weight |
:::footer |
TextBlock with isSubtle=true, size=Small |
:::action |
Action.OpenUrl entries in the card actions array |
:::callout |
Container with style=accent |
:::hero |
Image element with size=Stretch |
:::fields |
FactSet with title/value facts |
:::media |
Image element; altText from alt attribute |
:::columns |
ColumnSet with Column elements (width=stretch) |
:::highlight |
Container with style=“attention” containing a TextBlock |
:::centered |
Children rendered as normal card elements |
:::list |
TextBlock with list-formatted text |
:::raw |
Parsed JSON inserted as Adaptive Card element |
Example
::: header
Order Shipped
:::
Hey {{user.first_name}}, your order **#{{order.id}}** has shipped!
::: fields
Tracking ID: {{order.tracking_id}}
Delivery: {{order.delivery_date}}
:::
::: action
[Track Order]({{tracking_url}}){primary}
:::
::: footer
Order notification | {{company_name}}
:::Compiled output
The output is an Adaptive Card JSON structure:
{
"card": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Order Shipped",
"size": "ExtraLarge",
"weight": "Bolder"
},
{
"type": "TextBlock",
"text": "Hey Gaurav, your order **#ORD-123** has shipped!",
"wrap": true
},
{
"type": "FactSet",
"facts": [
{ "title": "Tracking ID", "value": "TRK-456" },
{ "title": "Delivery", "value": "Mar 30" }
]
},
{
"type": "TextBlock",
"text": "Order notification | Acme Corp",
"isSubtle": true,
"size": "Small"
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Track Order",
"url": "https://track.example.com/123"
}
]
}
}Native JSON mode
For Adaptive Card features beyond what directives cover (Input.Text, Input.Toggle, Action.Submit, ColumnSet with specific widths), use msteams.json:
{
"$norq": { "enabled": true },
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Rate your delivery",
"size": "Large",
"weight": "Bolder"
},
{
"type": "Input.ChoiceSet",
"id": "rating",
"label": "How was your experience?",
"choices": [
{ "title": "Excellent", "value": "5" },
{ "title": "Good", "value": "4" },
{ "title": "Average", "value": "3" }
]
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Submit Rating"
}
]
}Inline formatting
Inline HTML tags in templates are passed through as-is. Adaptive Cards do not render HTML – tags are stripped or ignored by the Teams client. Use Markdown formatting instead.
Adaptive Cards support a subset of Markdown:
| Markdown | Adaptive Card |
|---|---|
**bold** |
Rendered bold |
*italic* |
Rendered italic |
[text](url) |
Hyperlink |
- list item |
Bullet list |
1. list item |
Numbered list |
Partials
Partials are reusable template fragments included with {{> partial-name}}. For MS Teams, partials can also be .card.json files containing Adaptive Card JSON elements. When both header.card.json and header.md exist in the partials directory, .card.json is preferred for MS Teams compilation. Adaptive Card JSON partials support Handlebars control flow ({{#if}}, {{#each}}, etc.).
See Partials for directory layout and authoring details.
Best practices
- Adaptive Cards have a payload size limit – keep cards focused
- Use
:::fieldsfor structured data (renders as FactSet, which looks great in Teams) - Use
:::rawwith Adaptive Card JSON for interactive elements (inputs, choices) - Test with
norq preview <name> --channel msteamsto validate the card structure - Action.OpenUrl is the most compatible action type across Teams clients