Skip to main content
This page documents ABL’s rich content output formats: voice configuration, format-specific content (Markdown, Adaptive Cards, HTML, Slack, WhatsApp), carousels, interactive actions, feedback surveys, and reusable templates. For the expression language (operators, functions, {{}} interpolation syntax), see Expressions and functions.

Rich content

ABL supports multi-format output for delivering responses across different channels (web, mobile, voice, messaging platforms). The VOICE:, RICH_CONTENT:, and ACTIONS: blocks can be attached to any RESPOND statement, COMPLETE condition, or lifecycle handler. A pure reasoning agent (no FLOW:) may also declare top-level RICH_CONTENT: and ACTIONS: blocks that attach to its generated responses.

Overview

A single response can include:
  • Plain text — the default RESPOND string.
  • Voice configuration — SSML markup or natural language voice instructions.
  • Rich content — Markdown, Adaptive Cards, HTML, Slack Block Kit, WhatsApp, or AG-UI.
  • Carousels — scrollable card collections with images and buttons.
  • Interactive actions — buttons, select menus, and input fields.
  • Templates — reusable named response definitions with interpolation.
The runtime selects the appropriate format based on the delivery channel.

Voice configuration

Voice configuration provides channel-specific voice output. The VOICE: block can appear alongside any RESPOND.

Syntax

Voice properties

SSML example

Natural language instructions

For voice platforms that accept style instructions rather than SSML:

Rich content formats

The rich-content block provides format-specific variants of a response. The runtime selects the variant matching the delivery channel.
RICH_CONTENT: is the canonical block keyword; FORMATS: is the original spelling, kept as an accepted alias. The .agent.yaml format accepts rich_content: or formats: the same way. Most places a rich-content block is authored — directly under a RESPOND: (in a flow step, ON_START, HOOKS:, or an ON_ACTION/ON_ERROR handler), in a tool’s on_result:/on_error: action block, and in a COMPLETE: condition (including one with no RESPOND: at all) — accept either spelling interchangeably.

Syntax

Rich content properties

Rich content supports two kinds of properties. Channel-specific string formats carry a payload written for one channel. Structured template formats define content once, and the runtime renders it for each channel. Channel-specific string formats: Structured template formats (channel-neutral; the runtime renders them per channel): Structured templates describe content as data, such as a carousel, table, or chart. The runtime converts each template to the native format of the target channel. Templates fall into two groups: basic templates for simple display, and data-rich templates for structured data. Basic templates — display-only, no aggregation or layout options beyond their own items: Data-rich templates — designed to present structured/tabular data, with extra options like adaptive narrowing:
Named templates (in the TEMPLATES: block) may also declare a RENDERABLES: list — customer-owned structured payloads with name, payload, optional targets (api/sdk_websocket/http_async), fallbackText, and schemaRef. Several structured templates also support collection binding — from:/template: — to render one entry per array item; see Collection binding for exactly which ones.

Template modifiers

Two cross-cutting modifiers apply to structured templates:
  • when — a conditional render guard (expression). On an agent-level rich-content block, the template renders only when the expression is truthy at reasoning finalization. Available on the rich-content block and on each structured template. See Expressions and functions for expression syntax.
  • adaptive — on LIST, KPI, and TABLE templates whose items/rows are bound to a tool variable, set adaptive: true to let the reasoning agent narrow which items/rows/columns render for the user’s request.

Collection binding

Several structured templates have an array-valued property — CAROUSEL.cards, QUICK_REPLIES itself, LIST.items, TABLE.columns and TABLE.rows, CHART.data, and FORM.fields. Each of these accepts three forms:
  1. A literal array — write the items directly, as shown in every static example on this page.
  2. A from/template binding — resolve the array from a session variable at render time, and render one entry per array item:
    from names the session/tool-result path holding the array (the {{ }} wrapper is optional — both forms resolve identically). template is applied once per array item; inside it, {{ }} placeholders resolve against that item’s own fields merged over the outer session data — so an item shaped {name: "Equities", percentage: 45} is referenced as {{name}}/{{percentage}}, not {{item.name}}. If an array element isn’t an object (a bare number or string), it’s exposed as {{value}} instead.
  3. A bare string reference — when the tool result is already shaped exactly like the target items (for example, a tool that already returns {label, value, color} objects for a chart), skip template: entirely:
template is optional within a from binding — a binding with only from behaves like the bare-string form above, applied to the resolved array. options on a select-type action element (used inside FORM.fields and ACTIONS) is itself collection-bound the same way, so a select’s options list can also be tool-driven.

Carousels

Carousels display a horizontal scrollable collection of cards, each with a title, subtitle, image, and action buttons.

Syntax

CAROUSEL also accepts a when render guard, and cards supports collection binding — resolve the cards from a tool result instead of writing them statically:

Quick replies

Quick-reply chips give the user a small set of one-tap responses.

Syntax

Quick reply properties

QUICK_REPLIES itself supports collection binding — the whole list can come from from/template — but, unlike the other structured templates, it has no when guard: the property is the raw collection, with no wrapping object to hang a render guard on.

Lists

A vertical list of items, each with an optional subtitle, image, and tap action.

Syntax

List properties

List item properties

Dynamic example — one list item per branch a tool returns:

Media (image, video, audio)

IMAGE, VIDEO, and AUDIO share an identical shape — a single media item, not a collection.

Syntax

Media properties

IMAGE/VIDEO/AUDIO are each a single object — there’s no array, so collection binding doesn’t apply here. To show several images, use a LIST or CAROUSEL instead.

Files

A downloadable file attachment.

Syntax

File properties

KPIs

A single metric tile — a label, a value, and an optional trend indicator.

Syntax

KPI properties

KPI is a single value, not an array — there’s no collection binding here (that’s what distinguishes it from TABLE/LIST/CHART). To show multiple KPIs, author multiple KPI blocks or use a TABLE.

Tables

Columns and rows for tabular data.

Syntax

Table properties

Table column properties

Dynamic example — columns declared statically, rows bound to a tool result (the two bindings are independent, so it’s common to declare columns statically and bind only rows):

Charts

Pie, bar, or line charts, rendered as SVG on channels that support them.
Currently, charts render on the Web SDK only. Color is optional — the SDK provides a default palette for dynamic charts.

Syntax

Chart properties

Chart data point properties

Dynamic example — one data point per array item a tool returns:

Interactive actions

Interactive actions add buttons, select menus, and input fields to a response. Users interact with these elements, and the agent handles the interactions via ON_ACTION blocks.

Syntax

Action element properties

Select element example

Input element example

Form submission

When the ACTIONS block contains input elements, you can specify a submitLabel and submitId for the form submission button:

ON_ACTION handlers

Handle user interactions with action elements in flow steps:
The ON_ACTION handler properties are described below. If a handler RESPOND includes rich FORMATS: before terminal routing, that payload is forwarded as the fallback final channel payload. The terminal target’s own rich payload takes precedence.

Reusable handlers (ACTION_HANDLERS:)

ON_ACTION inside a flow step handles actions for that step only. To define reusable action handlers available across the whole agent, declare a top-level ACTION_HANDLERS: section. Each entry is keyed by the action element id and uses the same grammar as ON_ACTION (an optional CONDITION, shorthand RESPOND/SET/TRANSITION, or a canonical DO: block of ordered actions — SET, CLEAR, LOG, RESPOND, CALL, GOTO/TRANSITION/THEN, HANDOFF, DELEGATE, COMPLETE).

Forms

A form groups several action elements — typically input fields, plus a submit control — into one submission.

Syntax

Form properties

FORM doesn’t have its own submitId — submission is handled the same way as any other ACTIONS form submission; see Form submission and ON_ACTION handlers.

Progress

A progress bar or circle for a single value against a maximum.

Syntax

Progress properties

PROGRESS is a single value, not an array — there’s no collection binding here.

FEEDBACK surveys

A FEEDBACK block renders a rating/feedback card. Author it inside a RICH_CONTENT: (or FORMATS:) block.
Inline feedback is deprecated (DEPRECATED_INLINE_FEEDBACK). A FEEDBACK block without a FEEDBACK_TEMPLATE still compiles, but the compiler warns: “Inline feedback is deprecated and will be removed in a future version. Define a survey in the project Surveys area and reference it with FEEDBACK_TEMPLATE.” Prefer referencing a project survey template.

Referencing a survey template

Set FEEDBACK_TEMPLATE to a project survey name. The prompt and type (the survey’s measure) then come from the template, so they become optional here; any copy fields you do set act as per-string overrides (useful for translation):
When a template is named you may not override the measure — type, max, scoring, range, and comment_threshold are owned by the template, and setting them alongside a template is a compile error. Naming a template that doesn’t exist in the project is an error (UNKNOWN_SURVEY_TEMPLATE). expires_after is the one exception to that rule — a numeric answer-window TTL (in seconds) that is overridable per-agent even when a template is referenced:
The effective window is the agent’s expires_after if set, else the template’s own TTL, else a 300-second platform default — clamped to 30–86400 seconds either way. A survey presented to the customer can only be answered within this window; a submission after it expires is rejected.

Inline FEEDBACK (deprecated)

Without a template, prompt and type are required:

Condition-based wording (<field>_rules)

To vary wording by condition — the second way to translate a survey, kept next to the source copy — add a <field>_rules list. Each rule has a when expression and the field’s replacement text (the field key, or the alias value). Rules are ordered, first match wins:
Only wording fields accept rules: prompt, submit_label, pending_message, success_message, error_message, comment_prompt, comment_placeholder. There is no type_rules/max_rules — the measure is never rule-selectable.
Copy rules are evaluated before the customer answers, so a when expression may reference max, language, channel, and session (as session.<name>) but not rating. An invalid condition is a compile error (INVALID_SURVEY_CONDITION).

Post-transfer surveys (ESCALATE to a human agent)

When a human agent closes a conversation the bot escalated, the survey card shown on close is resolved from a FEEDBACK block — but that card renders with no bot turn running, so it can’t come from “whichever step the conversation happened to be on.” The platform instead picks one FEEDBACK block to stand for the whole agent:
  1. The agent-level block, if the agent declares one directly under its top-level RICH_CONTENT: (outside any FLOW: step) — this always wins when present.
  2. Otherwise, a FLOW step’s own FEEDBACK block. Both placements compile to the same thing: FEEDBACK nested under a step’s own RICH_CONTENT:, or under that step’s RESPOND:. The platform scans steps in FLOW: order:
    • Exactly one step has a FEEDBACK block → that one is used.
    • Several steps have a FEEDBACK block, naming the same FEEDBACK_TEMPLATE → treated as the same survey; the first step’s copy/wording wins.
    • Several steps have FEEDBACK blocks naming different templates, or any of them is an inline (template-less) block → ambiguous. No post-transfer survey is shown at all, rather than guessing which one you meant.
A FEEDBACK block’s when guard is not evaluated for this purpose — closing the conversation isn’t a turn, and when conditions over turn-scoped values (like input) would never hold outside one. A block nested deeper than the step itself — inside an ON_ACTION handler, a GATHER field, a digression, or a reasoning zone — is never a candidate; only the step’s own RICH_CONTENT:/RESPOND:-level block counts.
The transfer record stays pending after the human agent finishes and only closes once the survey is answered or its answer window (expires_after) runs out — whichever happens first. The resulting rating is stored with the survey template, the escalating agent’s name, and its version, so it appears correctly scoped in that survey’s Feedback Insights.

Templates

Templates are named, reusable response definitions declared in the TEMPLATES: block. They support {{}} interpolation and multi-format variants.

Syntax

Template properties

Referencing templates

Use TEMPLATE(name) in any RESPOND value:

Template interpolation

Template bodies use the same {{variable_name}} interpolation and {{#if variable}}...{{/if}} conditional-section syntax as any other ABL string — see Template strings in Expressions and functions for the full syntax, including calling built-in functions inside {{}}.

Channel selection

The runtime selects the response format based on the delivery channel. If the preferred format isn’t available, the runtime falls back through the priority chain until it finds a defined format. Plain text (RESPOND) is always the final fallback.
Related articles: