Skip to main content
Components move data in four directions: This page assumes you have already built a component. If not, start with Components.

Fetch data before showing

Fetch data before showing calls your API at the moment the assistant sends the component, and binds the JSON response so blocks can render it. Use it for anything that must be current, such as live pricing, the user’s open orders, or available inventory. Configure the request in the component’s ADVANCED section: a method (GET, POST, PUT or PATCH), a URL, and free-form rows for headers, query params and body. There is no separate auth section, so add an Authorization header row. The parsed response is bound to api_res: Inside a block prop you can interpolate a binding into a longer string, such as ₹${{plan.premium}}/mo. When the whole value is a single binding the raw typed value is kept, so arrays and objects survive intact.
Only agent variables resolve inside the request itself. In the fetch’s own URL, headers, query and body, ${{custom_args.<name>}} is the one binding that resolves. The builder also lists your component parameters as available there, but they are not populated at send time, so the request is skipped and logged as a failure. Keep parameters out of this request.
api_res is only a valid binding anywhere in the component when this fetch is configured. Without it, saving fails with an undeclared-source error.

Send data when a button is tapped

Call an API on response fires when the user taps a button, and sends what they tapped or typed. It takes the same request shape as the fetch above. These bindings are available in the URL, headers, query and body: Anything else is rejected when you save, so a typo surfaces immediately rather than failing silently at runtime. A single button can carry its own API call, which overrides the component-level one for that tap. The override is matched by Action ID, so a button inside a repeated list, whose Action ID is itself a binding, cannot be matched and falls back to the component-level request.

Send data when a form is submitted

There is no separate submit action. A button set to Send to agent collects the named inputs of its nearest ancestor form, or of the whole component if there is no form, exactly like HTML. That is what the inspector means by “Inside a Form, ‘Send to agent’ also submits the form’s inputs.” Required fields and email formats are validated in the browser first, over the inputs that are currently visible. Anything hidden by a condition is skipped. The assistant then receives:
To forward that to your own system, reference the input names in the request body:
A tap counts as one conversation turn, so the component locks once it has responded.

Fire an event on your page

Set a button’s when tapped to Page event and give it an Event ID. Tapping dispatches a CustomEvent on window, which your own page handles however you like: open a modal, scroll to a section, start a checkout.
Event IDs are lowercased, spaces become underscores, and only a-z, 0-9, _, - and : survive, so ringg:open_pricing is valid as typed.
A page event does not reach the assistant. It fires on your page only. If the assistant needs to know the user acted, use a Send to agent button instead.
Set when tapped to Open a link for anything that leaves the chat: a brochure, a policy PDF, a payment page, a phone number. A brochure download is the common case. Give the button a label, a URL, and a target:
If the brochure differs per user or per plan, have the fetch return the link and bind it: ${{api_res.brochure_url}}. Two rules the builder enforces when you save:
  • The scheme must be https:, mailto: or tel:. Plain http is rejected.
  • The URL must be either a plain literal or one whole ${{binding}}. A literal host with a binding spliced into it, such as https://cdn.example.com/${{api_res.id}}.pdf, is rejected. Return the complete URL from your API instead.
Like a page event, opening a link tells the assistant nothing. Pair it with a Send to agent button when the assistant should acknowledge the download or follow up on it.

Render an image

The image block takes a url, not src, plus optional alt, align, width and height. Give it a width or height, or it renders as a small thumbnail. Image URLs are held to the strictest rule of any prop: https only, and the same literal-or-single-binding rule as links. So a product shot driven by your API works:
A constructed URL does not. If your API returns an ID, have it return the full image URL alongside it.

What you can bind to

An input’s name is not a binding. You cannot show what the user typed in another block, so ${{full_name}} will not resolve. Input values only exist after submit, and only reach the assistant and your API. Bindings resolve from parameters, agent variables, the fetch response and repeat items.

Parameters

Parameters are the blanks the assistant fills in when it shows a component, such as the plans to compare or the order to display. Add them under Parameters, with a key, a description and a type of string, number, boolean, array or object. Keys are sanitized to a-z, 0-9 and _. Only dynamic parameters are described to the assistant, and its test value doubles as the example shape it is shown. Give each one a description that says what belongs in it, because that description is what the assistant reads. If a required dynamic parameter arrives missing or empty, the component is not sent and the assistant is told to say something instead, so a component never renders half-populated. Leave Parameters empty and the component simply uses conversation values as they come.

Agent actions

A component is not the only way to reach your page. The assistant can also dispatch a browser event directly, with no card or form involved, which suits things the user never needs to see or tap: focusing an input, opening a modal, scrolling to a section.
Where to set this up. Open your agent and go to Embed & Widgets → Legacy widgets → Execute DOM Action (execute_dom_action). Each entry under Actions is one event the agent can dispatch.
Each action has three fields: At runtime the agent decides during a conversation when to fire one of its configured actions, the widget dispatches a CustomEvent on window using the Event ID as the event name, and your listener runs. Actions only fire during an active conversation.
In React, remove the listener on unmount so navigation does not duplicate handlers:
While you are wiring things up, turn on eventLogs to see a pill in the chat thread each time an action fires. It is purely a visual aid, and actions fire the same way either way:
Pick distinctive Event IDs such as ringg_focus_search or shop_show_promo so your action names do not collide with other custom events on the page. Multiple listeners on the same Event ID all fire, and inside one handler you can route on event.type.
Looking for widget state events such as open, close, conversation start or feedback? Those are separate. See Widget Lifecycle Events.

Limits and QA notes

Bindings resolve and repeated blocks expand on the server, before anything reaches the browser, so the user never sees an unresolved ${{…}}.
A component resolves to at most 500 nodes. A repeat over a long list is capped by max_items, not by the source array.
Unresolvable optional props are dropped and empty containers are pruned, so a missing API field degrades quietly instead of rendering blank.
Test with the fetch pointed at your real endpoint. A request that fails means the component is sent without api_res, and any block that depended on it disappears.
Components render during voice calls too, but they are designed for chat. Confirm the flow still makes sense if the user is only listening.
After renaming a component, update every @[[…]] mention in the prompt. The rename is blocked until you do.
For agent actions, add an Event ID, attach a matching listener, and confirm it fires during a test conversation. If you configured a Default Payload, verify event.detail carries those fields.