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