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