# Channel Delivery Templates

When an Agentic Flow agent sends a message through a channel, a **delivery template** controls what that message looks like. Templates are configured on the **Output** node in the flow editor, once **Channel Delivery** is turned on.

You have three options — leave the template empty, write a fixed message, or write a template with placeholders. The right choice depends on how much control you need over the output.

## Quick Decision Guide

**Do you know what the message should look like?**

- Yes, same every time → write a **fixed template** with no placeholders. The agent is not involved.
- Yes, but the details change → write a **template with placeholders**. You define the layout; the agent fills in the values.
- No, let the agent decide → **leave the template empty**. The agent writes the full message. For more control over when to deliver, set Invocation to `agentic`.

**Is this a HITL approval card or regular delivery?**

- HITL approval → template must include Approve and Reject buttons. Only the 7 tokens in the [HITL placeholders table](#hitl-templates) are substituted.
- Regular delivery → no button requirement. Any placeholder token works; the agent fills it in.

**Which channels support structured templates?**

- **Adaptive Cards** — Microsoft Teams only (`adaptive_card` event type).
- **Email** — Outlook (`email`, `email_reply` event types). The template is a JSON object with `subject` and `body`.
- **Plain messages** — Telegram, Facebook, Universal Webhook, and Google Chat (`message` event type) deliver the agent’s response as text. The template is not applied to `message` events.

## The Delivery Targets Table

Open a flow in the Agentic Flow editor, select the **Output** node, and turn on **Channel Delivery**. The **Delivery Targets** table appears, with one row per destination.

| Column | Description |
| --- | --- |
| **Channel** | Where the message goes (Teams, Outlook, Telegram, etc.). |
| **Channel Type** | Auto-filled from the channel you select. |
| **Route Type** | Auto-filled (conversation, notification, etc.). |
| **Event Type** | The kind of message: `adaptive_card`, `message`, `email`, etc. For HITL rows this is locked to `hitl_approval`. |
| **Recipient** | Who receives it. Pick a named contact, or use `auto` to let the system resolve the recipient. |
| **Template** | The message layout in JSON. Paste an Adaptive Card or email template here. |
| **Invocation** | `automatic` — fires on every invocation. `agentic` — the agent decides whether and to whom to deliver. Default: `automatic`. |

With `automatic` invocation, the **Template** column appears after you have selected a channel and event type for that row. With `agentic` invocation, it appears as soon as the row exists.

## The Template Editor

Click the **</>** icon (**Open template editor**) next to the Template column header. The editor matches the row’s event type:

| Editor | Opens for | Preview |
| --- | --- | --- |
| **HITL Adaptive Card Template** | `hitl_approval` rows | Adaptive Card |
| **Delivery Adaptive Card Template** | Other card rows, such as `adaptive_card` | Adaptive Card |
| **Email Delivery Template** | `email` and `email_reply` rows | Email |

In each editor you can:

- Write or paste the template JSON. It is validated as you type, and **Save template** is blocked until the template is valid or empty.
- Load a built-in example with **Use example**, or **Copy** it from the **Example** panel.
- Remove the template with **Clear**.
- Check the live preview, which shows the template with sample values for placeholders. Click refresh to re-render it. The preview is hidden while the template is invalid, and shows **Preview unavailable** if the card cannot be rendered.

The HITL and email editors also list the placeholders they substitute.

> **Note**
>
> The live preview is a structural check, not a pixel-perfect render. Channels apply their own theme and button chrome at send time. Use the preview to verify structure and content, not exact styling.

## Three Ways to Write a Template

**Option A — Leave it empty**

For `message` events, the agent’s response is sent as a plain message. For `adaptive_card` and `email` events, the agent writes the full card or email itself. Best when you want the agent to fully control the output.

**Option B — Fixed message (no placeholders)**

Your JSON is sent exactly as written on every invocation. The agent is not involved. Use this for messages that always look the same — daily status cards, fixed notifications.

**Option C — Template with placeholders**

Define the layout using `{{token}}` placeholders. The agent (or the system, for HITL cards) fills in each token at run time. The structure and wording stay exactly as you wrote them; only the placeholder values change.

## Allowed Placeholders

### HITL Templates

Only the following 7 tokens are substituted in approval cards. Any other `{{token}}` appears as literal text in the delivered card.

| Token | What it shows | Example |
| --- | --- | --- |
| `{{title}}` | Headline from the Approval Title field. | `Action Required` |
| `{{description}}` | Short explanation from the Approval Description field. | `Please review before sending.` |
| `{{bot_response}}` | The agent’s proposed response — the approver can edit this before approving. | `Hi! Your refund of $42 has been processed.` |
| `{{execution_id}}` | Internal ID used to resume the flow after a decision. | `f87a3d0f-0c66-4c1b-8f30-e082297be365` |
| `{{original_message}}` | The user message that triggered the flow. | `Can you refund my last order?` |
| `{{sender_name}}` | Display name of the user who sent the original message. | `Alex Tan` |
| `{{source_channel}}` | The channel the original message came from. | `microsoft_teams` |

### Output Templates

For regular delivery, placeholders are filled by the agent. You can use any token name and the agent will populate it with a contextually appropriate value. The email editor lists these common tokens:

| Token | What it shows |
| --- | --- |
| `{{title}}` | A short headline derived from the agent’s response. |
| `{{bot_response}}` | The full agent response. |
| `{{sender_name}}` | Display name of the user who started the conversation. |
| `{{source_channel}}` | The channel the original message came from. |
| Any custom token | `{{priority}}`, `{{amount}}`, `{{order_id}}`, etc. — the agent fills these in based on the conversation context. |

> **Tip**
>
> To prevent a token from being substituted, write it as plain text without `{{ }}`.

## Template Examples

### Example 1 — HITL Approval Card

An approval card for a Human-in-the-Loop flow. Includes the original message, a proposed response the approver can edit, and Approve/Reject buttons.

```
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.5",
  "body": [
    {
      "type": "Container", "style": "accent", "bleed": true,
      "items": [
        { "type": "TextBlock", "text": "⚠ {{title}}",
          "size": "Large", "weight": "Bolder", "color": "Warning", "wrap": true },
        { "type": "TextBlock",
          "text": "From {{sender_name}} via {{source_channel}}",
          "size": "Small", "isSubtle": true, "spacing": "None" }
      ]
    },
    { "type": "TextBlock", "text": "{{description}}", "wrap": true, "spacing": "Medium" },
    { "type": "TextBlock", "text": "💬 {{original_message}}", "wrap": true, "spacing": "Medium" },
    { "type": "Input.Text", "id": "edited_response",
      "placeholder": "Edit the response before approving…",
      "isMultiline": true, "value": "{{bot_response}}" },
    { "type": "TextBlock", "text": "Ref: {{execution_id}}",
      "size": "Small", "isSubtle": true, "spacing": "Small" }
  ],
  "actions": [
    { "type": "Action.Submit", "title": "✅ Approve & Send",
      "style": "positive", "data": { "hitl_action": "approve" } },
    { "type": "Action.Submit", "title": "Reject",
      "style": "destructive", "data": { "hitl_action": "reject" } }
  ]
}
```

### Example 2 — Output Delivery Card

A delivery card for the Output node. No approve/reject buttons — just a formatted message.

```
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.5",
  "body": [
    {
      "type": "Container", "style": "emphasis", "bleed": true,
      "items": [
        { "type": "TextBlock", "text": "{{title}}",
          "size": "Large", "weight": "Bolder", "wrap": true }
      ]
    },
    { "type": "TextBlock", "text": "{{bot_response}}", "wrap": true, "spacing": "Medium" },
    { "type": "TextBlock", "text": "Sent via {{source_channel}}",
      "size": "Small", "isSubtle": true, "spacing": "Small" }
  ]
}
```

### Example 3 — Empty template

Leave the template field blank. On a `message` event, the agent’s response is sent as plain text. Best for short, conversational replies.

### Example 4 — Fixed card (no placeholders)

```
{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.5",
  "body": [
    { "type": "TextBlock", "text": "Daily summary ready.",
      "weight": "Bolder", "size": "Medium", "wrap": true }
  ]
}
```

Sent exactly as written on every invocation. No placeholder substitution, no agent involvement.

## Validation

The system validates your template when you save a delivery target row.

**HITL templates** — save is blocked unless all four checks pass:

| Check | What it means |
| --- | --- |
| Valid JSON | The template must be parseable JSON. |
| `"type": "AdaptiveCard"` | The root object must be an Adaptive Card. |
| Approve action present | The template must include an Approve button. |
| Reject action present | The template must include a Reject button. |

**Delivery card templates** — only valid JSON and `"type": "AdaptiveCard"` are required.

**Email templates** — must be a JSON object that includes `subject` and `body`.

An empty template is always valid.

If you use a token not in the HITL allowed list, you can still save. The token appears as literal text in the delivered card.

## HITL Configuration

HITL (Human-in-the-Loop) pauses a flow and sends an approval request to a reviewer before any message is delivered. Use it for high-impact actions — emails to customers, payments, anything that touches another system.

In an Agentic Flow, the approval card is configured in the **Approval Delivery Targets** table of the **Human In The Loop** node. For AI Coworkers, set the approval delivery target on the [Governance](../coworkers/governance.html) tab. See [Human in the Loop](../9_agentic_flow_builder/human_in_the_loop.html) for how approvals work.

**Template rules:**

- The template must include at least one Approve button and one Reject button.
- Tracking IDs (execution ID, approver identity) are added to each button automatically — you do not need to include them.
- Only the 7 tokens in the [HITL placeholders table](#hitl-templates) are substituted. Anything else appears as literal text.
- If the template is empty or invalid, the system uses a default card with Approve and Reject buttons only.

**After approval:** The flow resumes. If the approver edited the `edited_response` field, that text is used as the delivered message. If not, the original `{{bot_response}}` value is used.

**After rejection:** The flow resumes with a rejection signal and the proposed response is not sent.

> **Note**
>
> The HITL template controls the **approval card** sent to the reviewer. The Output node’s template controls the **final delivered message** to the end user. These are separate templates on separate nodes.
