Usher for web
BetaUsher is an assistant that lives inside your signed-in React web app. Your users talk to it or type to it, and on every turn it decides whether to answer, offer a page, take them there, or ask a clarifying question. It moves them through your own router and only ever to pages you’ve listed.
import { buildDestinationMap } from "@voqal/usher-core";import { Usher } from "@voqal/usher-react";import { reactRouterAdapter } from "@voqal/usher-react-router";import { router } from "./router"; // your existing createBrowserRouter(...)const adapter = reactRouterAdapter(router);const destinations = buildDestinationMap({ discovered: adapter.discover() });export function UsherAssistant() { return ( <Usher voqalKey={import.meta.env.VITE_VOQAL_KEY} router={adapter} destinations={destinations} /> );}That’s a working integration: Usher discovers your routes from the router. The quickstart adds the key, the two lines that render it, and the optional layers that make it better.
What Usher does
- A floating orb (bottom-right) starts a voice conversation. Its Aa button opens a chat panel. Voice and chat are one conversation with one context.
- It knows your app through a destination map: an id, a route pattern, a title, a description, and the phrases people use for each page. The model picks an id from that list. It never produces a URL.
- It can answer from a small knowledge base you pass in, and link an answer to the page it’s about.
- It respects roles. A destination with
roleScopesis hidden from users outside those roles. The model is never told it exists. - It can call your app’s own tools, like “create a draft invoice”, that you write in
usher.tools.ts. They run in the browser as the signed-in user. Every key. See Tools.
| Decision | Example | When |
|---|---|---|
| Navigate | “Take me to billing” | Imperative phrasing, one confident destination, and you aren't already on it. |
| Offer | “How do I book a consult?” | Answers, then offers to take you there. You confirm before anything moves. |
| Answer | “What's your refund policy?” | Nothing to navigate to, or you're already on the page. Replies in a sentence or two. |
| Clarify | “Open settings” | Two destinations are about equally likely. Asks one short question. |
| Refuse | A page this user can't reach | The page doesn't exist or is out of role (the user isn't told which), or the target is unresolved, unsafe, or rejected by your router. It says so plainly. Nothing moves. |
How a turn works
Your router adapter supplies the route tree. Your manifest adds titles, descriptions, aliases, and roles. buildDestinationMap merges the two into the destinations you pass to <Usher>. On each turn the model chooses a tool (navigate, offer_navigation or clarify) or simply replies. Before anything moves, a deterministic validation chain checks the choice: the id must be listed, the role must allow it, every dynamic param must come from a safe source, and the final path must be relative. Only then does Usher call your router, so your route guards still run.
/cases/:caseId, the caseId comes from the selected entity in context, the current URL, or your own resolveEntity function. A value the model invents is ignored. See Dynamic params.Packages
Install the React bindings, the core, and the one adapter that matches your router.
| Package | Install | What it gives you |
|---|---|---|
@voqal/usher-react | Required | The <Usher> component: the orb, the chat panel, voice audio, and the imperative handle. |
@voqal/usher-core | Required | Types, buildDestinationMap, the validation chain, the policy, and the session runtime. Framework-agnostic. |
@voqal/usher-react-router | One adapter | reactRouterAdapter(router) for React Router 6.4+ and 7 data routers. Discovers routes and navigates through router.navigate(). |
@voqal/usher-next | One adapter | nextAdapter(router, options) for Next.js. Navigates with router.push(); you pass the route list. |
Requirements
- React 18 or newer (
reactandreact-domare peer dependencies). - A client-side router: React Router 6.4+ or 7 with a data router (
createBrowserRouter), Next.js 13+, or your own adapter. - A publishable key from Voqal. It works from any website unless you ask Voqal to lock it to your domains.
- ES modules. The packages ship ESM with type definitions and no CommonJS build.
- For voice: a secure context (HTTPS or localhost), microphone permission, and AudioWorklet support in a current browser.
What’s in the beta
This is Usher 1.0.0 Beta. Install it with npm install @voqal/usher-react @voqal/usher-core @voqal/usher-react-router (swap the adapter for Next.js).
- On every key: navigate, offer, answer (including from your knowledge base), clarify and refuse. Your app’s own tools. Questions about what’s on screen, answered by reading the page. Voice (hands-free or push-to-talk) and the chat panel on one session hosted by Voqal with your publishable key. Themes, role scoping, safe dynamic params, and the React Router and Next.js adapters.
- The voice is set on Voqal’s side: Voqal’s default voice, in US English, today. The voice model, voice and language can change (for everyone, or for your key on request) with nothing to update in your app.
- Powered by Voqal. A small “Powered by Voqal” line under the chat input is shown by default. It can be removed on request or on paid plans; it’s set on Voqal’s side, so there’s nothing to change in your code.
- Not included: tabs and modals that have no URL. A dashboard for editing your map: the map and knowledge base live in your code.
- The API can change between beta releases, so read the release notes before you upgrade. Update your privacy notice to say the assistant may read the current page.
How updates reach you
How the assistant behaves is set by Voqal’s hosted service, not by the package you installed. Each session Usher starts with your voqalKey gets Voqal’s current instructions: how it talks, when it offers a page instead of opening it, what counts as a yes, and how it recovers when the model goes quiet. When Voqal improves them, every session opened afterwards uses the new version. You don’t upgrade, rebuild, or redeploy.
- Upgrade the package for new UI, new props, and new features. The release notes say when a version needs one.
- Your layer stays yours. Your
title,instructions, andknowledgeare still added to every session, below Voqal’s rules. They can shape the tone and the facts, but they can’t switch off the safety rules. - The safety checks stay in the package. Whatever the instructions say, Usher only opens pages in your destination map, checks roles, and never takes an id from the model. Your router guards still run last.
- It never breaks a session. If the hosted instructions can’t be loaded or read, Usher uses the version built into the package and carries on.
- Self-hosting with your own
voice.fetchGrantuses the built-in version, which changes only when you upgrade.
The code updates too
<Usher> is a small, stable loader. The assistant itself (the orb, the panel, the session, page reading and your tools’ runner) comes from Voqal when the page loads, so fixes to the browser code reach your app too, with no npm update. Your code doesn’t change: the props, tools and the ref stay the same.
- Verified before it runs. The loader runs Voqal’s code only after checking two signatures against public keys in the package. One signature is on the release; the other, renewed automatically and good for 14 days at most, says it is current. It also checks the code’s hash, and then runs exactly those bytes.
- Updates arrive automatically. Each page load gets Voqal’s current runtime, signed and verified as above before it runs. If it can’t be loaded or verified, the assistant doesn’t start, and the rest of your page is unaffected.
- Your React. It uses your app’s React (18 or 19) and renders nothing on the server, so there’s no hydration mismatch.
Next steps
Quickstart
Install, add your key, and get the orb running in a React Router app.
Destinations
The route map: patterns, aliases, roles, dynamic params, and fallbacks.
Configuration & theming
Every <Usher> prop, safe context, knowledge base, and orb finishes.
Voice & chat
Interaction modes, session states, and how each decision behaves.
Tools
Your app’s own actions in usher.tools.ts, on every key.
Framework adapters
React Router, Next.js App Router and Pages Router, or a custom router.
Events & errors
Every decision Usher reports, refusal reasons, and error classes.
Security
The validation chain, what data leaves the browser, and CSP.
Troubleshooting
The orb doesn't move, voice won't start, the wrong page opens.
