> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kapso.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent UI skills

> Let an agent node send buttons, lists, WhatsApp Flows, and other rich messages

A UI skill is a WhatsApp message format plus instructions for when to send it. Each enabled skill becomes a `send_ui_<key>` tool on the [agent node](/docs/flows/step-types/agent-node). When the conversation matches the instructions, the agent calls the tool with the message content. Kapso validates it and sends it to the current conversation.

## Add a skill

1. Open an **AI Agent** node and go to the **Tools** tab.
2. Under **UI Skills**, click **Add skill**.
3. Fill in the fields and click **Save skill**.
4. Save the workflow.

| Field | Description |
| - | - |
| **Name** | Up to 100 characters. The agent sees it together with the instructions |
| **Message format** | One of the nine [message formats](#message-formats) |
| **Instructions** | When to send the message and where its content comes from. Up to 4,000 characters |
| **Fixed destination URL** | URL button only. Optional HTTPS link. Leave it empty when a tool supplies the destination |
| **Published WhatsApp Flow** | WhatsApp Flow only. See [WhatsApp Flow skills](#whatsapp-flow-skills) |
| **Opening screen ID** | WhatsApp Flow only. Optional, defaults to the first screen |
| **Wait for a reply** | Pause the agent after sending and continue when the customer responds. See [Wait for a reply](#wait-for-a-reply) |
| **Stable key** | Under **Skill identifier**. The tool is named `send_ui_<key>`. 1–40 lowercase letters, digits, or underscores, starting with a letter. Unique per node |

The key defaults to a random `skill_...` value. The agent sees the tool name, so a descriptive key like `choose_service` helps.

Use the switch next to a skill to enable or disable it. Disabled skills are not given to the agent. A node can have up to 20 skills.

The **Format preview** in the dialog is illustrative. The agent writes the real message when it runs.

## Message formats

The agent fills in these fields when it calls the tool. If a required field is missing or a limit is exceeded, nothing is sent and the agent gets the error back.

| Format | `component_type` | The agent supplies |
| - | - | - |
| Reply buttons | `interactive_reply_buttons` | `body`, 1–3 `buttons` with `id` and `title` |
| List | `interactive_list` | `body`, `button` label, 1–10 `rows` with `id`, `title`, and optional `description` |
| URL button | `cta_url` | `body`, `button` label, `url` (not asked for when the skill has a fixed URL) |
| WhatsApp Flow | `flow` | `body`, `button` label, optional `data` to prefill the opening screen |
| Image | `image` | `image_url`, optional `caption` |
| Location | `location` | `latitude`, `longitude`, optional `name` and `address` |
| Request location | `location_request` | `body` |
| Carousel with links | `carousel_url` | `body`, 2–10 `cards`, each with `image_url`, `body`, `button`, and `url` |
| Carousel with replies | `carousel_quick_reply` | `body`, 2–10 `cards`, each with `image_url`, `body`, `button`, and `id` |

Limits:

* `body` and `caption`: up to 1,024 characters. Card `body`: up to 160 characters and 2 line breaks.
* `button` labels and reply button `title`: up to 20 characters. List row `title`: 24. Row `description`: 72.
* `id`: up to 188 characters for buttons and list rows, 256 for carousel cards. IDs must be unique within a message, and so must reply button titles.
* `url` and `image_url`: HTTPS only, no credentials in the URL. Up to 2,048 characters, or 256 for a carousel card `url`.
* `latitude`: -90 to 90. `longitude`: -180 to 180. `name`: up to 256 characters. `address`: up to 1,024.

## Write instructions

The agent sees each skill as a tool described by its name and instructions. When a node has UI skills, Kapso also tells the agent to use them when their instructions apply, not to repeat the message as text, and to treat reply data as customer input.

Instructions should say:

* **When** to send the message.
* **Where the content comes from.** Name the tool that returns the IDs, labels, URLs, image URLs, or coordinates, or write the fixed values. The agent should not make them up.
* **What the message says**, if the wording matters.

Examples:

* **List**: When the customer wants to book, call `list_services` and offer up to 10 services. Use each service's `id` as the row ID and its name as the title. Put the price in the description.
* **Reply buttons**: After summarizing the booking, ask the customer to confirm. Use the buttons `confirm` ("Confirm") and `change` ("Change").
* **URL button**: When `create_payment_link` returns a link, send it with the button "Pay now". Never write the URL yourself.
* **Location**: When the customer asks where a store is, call `find_store` and send that store's coordinates, name, and address.
* **Request location**: When the customer asks for the nearest store and hasn't shared a location, ask them to share it.

`list_services`, `create_payment_link`, and `find_store` stand for your own [function, webhook, or MCP tools](/docs/flows/step-types/agent-node#custom-tools).

Selections come back with the IDs the agent sent, so prefer stable IDs from your own data.

## Wait for a reply

With **Wait for a reply** on, a successful send pauses the agent, the same as `enter_waiting`. Other tool calls from the same model turn don't run. The customer's next message resumes the same execution at the same node, with the conversation context intact.

It's on by default for reply buttons, lists, WhatsApp Flows, location requests, and carousels with replies. With it off, the agent keeps working after the send.

The tool call is the whole message. Text the agent writes alongside it, or later in the same turn, is not sent separately.

### What the agent receives

When the node has UI skills, replies that carry structured data reach the agent with a `<whatsapp_reply>` JSON block after the message text:

```text theme={null}
Selected: Haircut
<whatsapp_reply>{"interactive_type":"list_reply","reply_option_id":"svc_haircut","reply_option_title":"Haircut","context_message_id":"wamid.HBgM...","message_id":"wamid.HBgL..."}</whatsapp_reply>
```

| Key | Description |
| - | - |
| `interactive_type` | `button_reply`, `list_reply`, or `nfm_reply` (Flow submission) |
| `reply_option_id`, `reply_option_title` | ID and title of the selected option |
| `context_message_id` | WhatsApp ID of the message the customer replied to |
| `flow_response`, `flow_token`, `flow_name` | Flow submissions. `flow_response` holds the submitted fields |
| `latitude`, `longitude`, `location_name`, `location_address` | Shared locations |
| `message_id` | WhatsApp ID of the reply |

If the customer types instead of tapping, the agent gets their text as usual.

## WhatsApp Flow skills

Pick the Flow under **Published WhatsApp Flow**. It must be:

* Published and synced in Kapso. Use **Sync flows** in **WhatsApp** > **Flows** to import Flows created elsewhere. See [Flows in Kapso](/docs/whatsapp/flows/kapso-integration).
* On the same WhatsApp Business Account as the conversation's number. Otherwise the send fails.

**Opening screen ID** sets the screen the Flow opens on. Leave it empty to use the first screen in the latest synced Flow JSON. Saving checks that the screen exists.

Kapso opens the Flow with `flow_action: navigate` in published mode, with a new random `flow_token` on every send.

The agent can prefill the opening screen through `data`. It only accepts keys declared in that screen's `data`, with matching types. Say in the instructions which known values to prefill:

```text theme={null}
When the customer wants to book, open the booking form. Prefill customer_name
with the name returned by get_customer. Leave the other fields empty.
```

Submitted fields reach the agent in `flow_response`. See [What the agent receives](#what-the-agent-receives).

<Warning>Flows that need your data endpoint to load the first screen (`data_exchange` on open) are not supported. Dynamic Flows can still call your endpoint after the Flow opens.</Warning>

If the Flow is unpublished later, or its synced JSON no longer has the opening screen, Kapso leaves the skill out of the agent's tools.

## Test

Open **Test workflow** and chat with the agent. UI skill messages show up in the test chat. Nothing is sent to WhatsApp.

* Click reply buttons, list rows, and carousel replies to answer.
* URL buttons and carousel links open their destination. Images show inline.
* For a Flow, enter the submitted fields as a JSON object, like `{"service_id": "haircut"}`, and click **Submit form results**.
* For a location request, enter a latitude and longitude and click **Share test location**.

Each test reply reaches the agent with a `<whatsapp_reply>` block, like a real reply.

The test chat doesn't render the real Flow or check that Meta accepts the message. Test Flows on a real phone before going live.

## Definition JSON

In workflow definitions (Platform API, or `definition.json` with the CLI), skills live in `config.ui_skills` on the agent node:

```json theme={null}
{
  "node_type": "agent",
  "config": {
    "system_prompt": "Help customers book appointments.",
    "provider_model_id": "uuid",
    "ui_skills": [
      {
        "key": "choose_service",
        "title": "Choose a service",
        "component_type": "interactive_list",
        "instruction": "When the customer wants to book, call list_services and offer up to 10 services. Use each service's id as the row ID and its name as the title.",
        "enabled": true,
        "wait_for_reply": true
      },
      {
        "key": "booking_form",
        "title": "Booking form",
        "component_type": "flow",
        "instruction": "After the customer picks a service, open the booking form. Prefill service_id with the selected service ID.",
        "enabled": true,
        "wait_for_reply": true,
        "flow_id": "1197715005513101",
        "flow_screen": "BOOKING"
      }
    ]
  }
}
```

| Field | Type | Description |
| - | - | - |
| `key` | string | Required. 1–40 lowercase letters, digits, or underscores, starting with a letter. Unique per node. The tool is named `send_ui_<key>` |
| `title` | string | Required. 1–100 characters |
| `component_type` | string | Required. One of the [message formats](#message-formats) |
| `instruction` | string | Required. 1–4,000 characters |
| `enabled` | boolean | Required |
| `wait_for_reply` | boolean | Required |
| `url` | string | `cta_url` only. Optional fixed HTTPS destination |
| `flow_id` | string | `flow` only. Required. Meta's Flow ID, digits only |
| `flow_screen` | string | `flow` only. Optional opening screen ID |

* Up to 20 skills per node. camelCase keys like `componentType` and `waitForReply` are also accepted.
* Saving rejects an enabled skill whose `send_ui_<key>` name matches a function, app integration, or webhook tool on the same node. MCP tool names are only known at runtime, so a match there stops the agent with an error naming the tool. Rename the tool or the skill key.
* Saving a `flow` skill checks that the Flow is published in the project and synced, and that `flow_screen` exists.
* Skills are kept when you export, import, or duplicate a workflow. A `flow` skill imported into another project needs the same Flow published there.

## Limitations

* Workflows in [observer mode](/docs/flows/triggers#whatsapp-event-trigger) can't send UI skills.
* The workflow needs a WhatsApp conversation. The tool always sends to the current conversation from its number. It can't choose another recipient or number.
* Sends follow the same outbound permissions, billing, and 24-hour customer service window as any other non-template message. See [Meta message billing](/docs/whatsapp/meta-message-billing).
* When validation or the send fails, the agent gets a tool error and the workflow doesn't pause.
* Delivery is not exactly-once. Kapso won't resend a tool call it already recorded as sent, but a failure right after Meta accepts a message can leave the outcome unknown.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.