Skip to main content
Version: next

Tool handlers & deep links

The bot has two kinds of tools.

  1. Server-side catalog tools (search_catalog, get_product, find_similar_products, compare_products, list_subcategories, list_top_categories, site-nav resolution, end_session) run entirely on the backend. The bot speaks the result; your app does nothing.
  2. Client-executed actions (open a product, add to cart, apply a filter, …) are pushed to the SDK as an action message. The SDK runs your handler or fires a deep link, then sends the action_ack back to the backend for you.
Mobile is DOM-less

The mobile profile has no DOM to manipulate, so UI actions map to either a deep link (navigation) or a native callback (mutate your cart, set a filter). You register these; the SDK wires the acknowledgement.

Web host bridge (browser SDKs)​

On the web — the @monoverse/voicebot-{react,vue,next} embeds and the vanilla loader — client actions are routed through window.VoiceBotSyncBridge. You provide typed handlers (directly, or via the SDK onAction prop); the widget resolves them in precedence order and sends the action_ack for you. This is host-agnostic — a non-WooCommerce storefront wires its own cart / variant / filter handlers and the bot acts through them exactly as it would on WooCommerce.

The bot only offers actions your host has the capability for, and writes are confirmation-gated and server-authoritative. The full model — the capability vocabulary, declaring a host_profile, pushing variant data, and the bridge / onAction shapes with a worked example — lives in Capabilities & host actions. Wire bridge.selectVariant / onAction.selectVariant for variant selection — not toolHandlers.select_variant (that never fires).

The rest of this page covers the mobile profile (React Native / Flutter), which is DOM-less and maps the same actions to deep links or native callbacks.

// Native callback — mutate the cart, apply a filter, etc.
client.registerToolHandler('apply_filter', (args) => {
productList.setFilter(args);
return { success: true };
});

// Deep link — navigate via your router. Returns a route URI.
client.registerDeepLinkMap('open_product', ({ id }) => `myapp://product/${String(id)}`);
client.registerDeepLinkMap('view_cart', () => 'myapp://cart');

Deep links open through React Native Linking by default — override with a deepLinkOpener if you route differently.

The action table​

Each action the backend can push, and how to map it on mobile. This is the authoritative mapping from the wire protocol §9.

actionMobile mapping
open_product / navigate / open_category / view_cart / open_checkoutdeep link (registerDeepLinkMap) → your route; fallback native callback
add_to_cart / remove_from_cart / update_cart_qtynative callback (your cart API); executes after conversational consent (no extra button gate); no handler → { ok: false, error: "no_handler" }
select_variantnative callback (drive your variant selector); reversible, not confirmation-gated
apply_filter / filter_by_attribute / clear_filternative callback (your product list)
highlight / scroll_tonative callback, or a no-op ok: true if the list is not addressable
show_toastnative callback (snackbar)
submit_formserver-side plugin callback (unchanged), or a native override; confirmation-gated (requires explicit user interaction)
submit_checkouthighlights the submit control and waits for a click — never auto-submits (confirmation-gated, non-overridable)
open_popup / switch_languagenative callback, or disabled

no_handler — safe degradation​

An action with no registered handler never crashes your app. The SDK auto-replies:

{ "success": false, "error": "no_handler" }

and the bot adapts — for example, it stops offering to do that action and steers the conversation elsewhere. You only need to register the actions your app actually supports. (The handler-result key the SDK sends back is success on the client side; on the wire it is reflected into the action_ack ok field — see §7.)

You can also declare your capabilities up front so the bot only offers what you can execute — the SDK sends the set of registered handler names in the hello.capabilities frame (see §10).

How an action flows​

bot decides to open a product
│
▼
server executes the tool, pushes: { "type":"action", "action":"open_product", "action_id":"…", "params":{ "id": 42 } }
│
▼
SDK looks up "open_product"
├─ deep-link map → resolve URI → open via Linking / emit on deepLinks
└─ tool handler → run your callback → get a result
│
▼
SDK sends: { "type":"action_ack", "action_id":"…", "ok": true, "result": {…} }
│
▼
(no handler registered) → SDK sends ok:false, error:"no_handler"; bot adapts

See also​