---
title: "Widgets"
description: "Build interactive cards that appear under your agent’s replies after an API action or when a widget condition matches."
---

:::note[Plan]
Actions are available on the **Starter** plan and above.
:::

A widget is an interactive card under an agent reply. It can show an order status, a list of products, collect information, or give visitors buttons to choose what happens next. You build it for one agent under **Build** > **Widgets**. An enabled widget can appear after a successful API response from a [Custom API action](/docs/actions/custom-api), or on its own when its **When to show** condition matches. Several actions can use the same widget.

[Start from a template](/docs/widgets/building-widgets#start-from-a-template) to fill in a widget’s code, fields, examples, functions, and states without AI or credits.

Visitors see the card and its buttons, alongside the agent’s answer. For example, a reply about an order can include this card:

```jsx
<Card>
  <Title value="Your order" />
  <Text value={order.status} />
</Card>
```

## Show a widget

There are two ways a widget can appear:

| How it appears | What happens |
| --- | --- |
| From a Custom API action | The action calls your API through a saved [API connection](/docs/agents/api-connections). It shows the selected widget after a successful JSON response if the widget is enabled. Failed requests and text responses do not show a widget. |
| From a widget condition | Enable the widget, then set **When to show** in its **Settings** tab. When a visitor's message matches, the widget can appear without a Custom API action. |

The widget’s **Fields** define which data reaches visitors. Chatvox takes data in this order:

1. The widget’s **Default** example supplies starting values.
2. The Custom API action's input fields replace those values where provided.
3. The API response data, selected by the action’s **Data path**, replaces those values where provided.

Each source is restricted to the widget’s declared fields before merging. Nested objects merge key by key. Lists and individual values are replaced. The response wins when sources provide the same value.

For example, declare `order.status` and `order.number`. A default example can provide:

```json
{
  "order": { "status": "Preparing", "number": "Not provided" }
}
```

If the API supplies `{"order":{"status":"Shipped","number":"A-1234","internal_note":"Review"}}`, the visitor receives `status` and `number`, but not `internal_note`.

:::warning
Declaring an **Object** or **List** field permits its whole value, including nested data. Declare narrow paths such as `order.status` when you need to exclude other properties. Keep default examples safe for visitors: they also supply live data.
:::

**What the AI can see** controls the JSON response fields used in the agent’s answer: **All fields** or **Only selected fields**. The widget uses its own fields independently. Restricting the AI’s fields does not restrict the widget’s fields.

Named examples, such as a shipped-order example, only affect the builder preview.

## How widgets use the allowance

Each agent has one **Actions & Widgets per agent** allowance. Enabled actions and enabled widgets with their own nonempty **When to show** condition share it.

| Configuration | Slots used |
| --- | ---: |
| Enabled action, including an MCP Server action with multiple tools | 1 |
| Enabled standalone widget with its own **When to show** condition | 1 |
| Enabled Custom API action with a linked, enabled widget and no widget condition | 1 total |
| The linked widget also has its own **When to show** condition | 2 total: 1 for the action and 1 for the widget condition |
| Enabled widget used only by a Custom API action, with no widget condition | 0 extra |
| Additional server functions on a widget | 0 extra slots |
| Saved but disabled action or standalone widget | 0 active slots |
| Reusable API connection | 0 |
| **Escalate to Human** (requires **Standard** or above) | 1 |

An enabled widget that appears on its own when visitors ask about available times uses one slot. A linked widget without its own **When to show** condition uses no extra slot. Its enabled Custom API action uses one slot. Add a condition and the widget uses a second.

At capacity, you can save a disabled widget or an enabled widget without its own **When to show** condition. The latter can still appear through an enabled, entitled Custom API action. You can configure a new Custom API action as a disabled draft, then enable it after a slot opens. The Actions and Widgets pages show the same used and allowed count for this agent.

## How widget code stays limited

Widget code looks like JSX, but Chatvox never runs the source as JavaScript. The builder turns it into a component tree. The server checks that tree against an allowlist on every save. The visitor’s browser draws the checked tree using the fixed [component library](/docs/widgets/components).

Field reads, `.map()` loops, template strings, comparisons, `&&`, `||`, `!`, and ternaries are supported. Arbitrary JavaScript, scripts, and other function calls are refused.

Visitors never receive the API endpoint, request headers, credentials, widget source, or named examples. They do receive the declared data, rendered component tree, states, and supported function settings. Keep secrets out of text, default data, Open link URLs, and messages.

## Where widgets appear

Widgets appear in the web widget, Public Chat, and the Playground. They do not render on WhatsApp, through the REST API, or in an action's test request. Custom API actions can still call their API on WhatsApp and through the REST API; those channels do not display the widget. A widget's **When to show** condition is available in the web widget, Public Chat, and Playground.

Widgets use the chat theme’s colors and the agent’s accent color. Public Chat supports **Light**, **Dusk**, and **Midnight** themes. The builder preview stays light.

## Saved state and reply copies

Functions can set local variables or dismiss a widget. Chatvox saves those changes with that specific reply, so they survive a conversation reload. A **Send message** function adds a message as the visitor.

Each reply keeps its own copy of the widget and its data. Editing or deleting a widget or action does not change cards in earlier replies. In **Activity** > **Logs**, you can inspect a read-only copy labeled **Shown to the visitor** or **Dismissed by the visitor**.

**[Build a widget](/docs/widgets/building-widgets)**

Create, preview, and show a widget.

**[Component reference](/docs/widgets/components)**

Choose components and their properties.

**[Functions and states](/docs/widgets/functions-and-states)**

Define button behavior and conditional sections.

**[Product search example](/docs/widgets/product-search-example)**

Build a product card connected to an action.
