Skip to main content
Version: v1

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.

// 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); no handler → { ok: false, error: "no_handler" }
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)
set_delivery_method / set_payment_method / update_order_note / confirm_ordernative callback if implemented, else disabled (user_must_confirm)
submit_formserver-side plugin callback (unchanged), or a native override
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​