Skip to main content
Version: next

Quickstart

Three routes, one assistant. They are independent — do them in any order, skip what you don't need. Which credential each route uses is one table in API keys.

RouteYou needTime
A. Website widgeta pk_ publishable key~5 minutes
B. Backend catalog synca one-time pair code (or your pk_)~30 minutes
C. Mobile appa vb_pk_ publishable key (or your server)~30 minutes
The widget needs data to talk about

Route A mounts the widget; route B feeds it your catalog. Until a backend sync runs, the assistant has nothing real to say — wire both for a live store.

A. Website widget

Get a publishable key pk_… from your VoiceBot cabinet (Integrations) and register the exact origins allowed to use it. Then drop one tag anywhere in your page:

<script
src="https://api.monoverse.tech/widget/widget-voice.js"
data-public-key="pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
async
></script>

The deployed bundle self-mints an origin-locked session and mounts the launcher. That's the whole integration for a vanilla site. Shipping a framework?

  • Reactnpm install @monoverse/voicebot-react
  • Vuenpm install @monoverse/voicebot-vue
  • Next.jsnpm install @monoverse/voicebot-next
  • Vanilla JS — the script tag above, plus optional attributes

Custom storefront with its own cart? Continue with Custom site integration to wire cart/variant actions.

B. Backend catalog sync

Every producer follows the same shape — pair once, push a full snapshot, then deltas:

  1. Pair — exchange a one-time VB-XXXX-XXXX code from the cabinet (or your pk_ via pair-by-key) for a per-tenant HMAC shared secret, stored encrypted.
  2. Full snapshot — stream every product, category, and page as gzipped NDJSON.
  3. Deltas — push small signed batches as your data changes, on your own schedule.

Pick your stack:

The umbrella page is Backend — get your catalog in.

C. Mobile app

Get a text session running in about 30 minutes, then add voice. Pick your SDK — the rest of this section follows your choice.

Text works immediately. Voice needs a real device.

The text path uses zero native code and runs in a simulator. Voice requires a real device plus the native audio module (an emulator microphone is unreliable). Voice failure is non-fatal — the text path keeps working.

C1. Get your publishable key

The fastest start is Mode B (publishable key) — no backend required. In your VoiceBot cabinet, go to:

Cabinet → Integrations → Mobile app

Copy the publishable key — it looks like vb_pk_…. It is an identifier, not a secret, and is safe to ship in your app binary (like a Stripe pk_). Add your app's bundle id / package (e.g. com.yourco.app) to the allowlist on the same screen.

It is the same VoiceBot key that authorizes your website widget — one key for web + app. For production apps that already have a backend, prefer Mode A (server-minted) so no key is embedded at all. Both modes are covered in Auth & API keys.

C2. Install

npm install @monoverse/voicebot-react-native react-native-nitro-modules
# iOS:
cd ios && pod install

The native audio module uses the New Architecture (default on RN 0.76+). Text chat works without it; voice does not.

Expo: this SDK ships a native module, so it does not run in Expo Go. Use a dev build:

npx expo install @monoverse/voicebot-react-native react-native-nitro-modules expo-dev-client
app.json
{ "expo": { "plugins": ["@monoverse/voicebot-react-native/app.plugin"] } }
npx expo run:ios # or: npx expo run:android

The config plugin wires NSMicrophoneUsageDescription (iOS) and RECORD_AUDIO (Android).

C3. Init the client

Init is synchronous and does no network — it just configures the client.

import { VoicebotClient } from '@monoverse/voicebot-react-native';

const client = VoicebotClient.init({
baseUrl: 'https://api.monoverse.tech',
auth: { mode: 'publishableKey', apiKey: 'vb_pk_…', appId: 'com.yourco.app' },
});

C4. Map the bot's UI actions

The bot runs catalog lookups server-side and just speaks the result. For UI actions (open a product, add to cart, apply a filter) it pushes an action; the SDK runs your handler or fires a deep link, then acknowledges the backend for you. An action with no registered handler degrades safely — the bot adapts, your app never crashes. Full table in Tool handlers & deep links.

client.registerToolHandler('add_to_cart', async ({ id, qty }) => {
await myCart.add(String(id), Number(qty));
return { success: true, cartItemCount: myCart.count };
});
client.registerDeepLinkMap('open_product', ({ id }) => `myapp://product/${String(id)}`);

C5. Start a session and listen

This is the launcher action — wire it to your "open assistant" button.

const session = await client.startVoiceSession({ lang: 'en' }); // any supported language code

session.on('transcript', (t) => console.log(t.role, t.text)); // works on the text path alone
session.on('status', (s) => console.log(s.status));
session.on('error', (e) => console.warn(e.code, e.message));
session.on('ended', (e) => console.log('ended', e.reason));

// A text turn works right now — no microphone needed:
session.sendText('how much is the laptop?');

C6. Add voice

Voice needs a real device and the native audio module. Failure is non-fatal — text keeps working.

// startVoice() requests the mic permission for you, then captures.
// Denial is non-fatal — it rejects and text keeps working.
session.startVoice().catch((err) => console.warn('voice unavailable', err));

session.setMuted(true); // mute the mic mid-session
session.minimize(); // collapse the UI — session STAYS ALIVE (no end sent)
session.restore(); // un-collapse
await session.endSession(); // user closed the chat → real end + teardown
minimize ≠ close

minimize() is UI-only — the session and WebSocket stay alive. Only endSession() ends the session. Wire your "collapse" control to minimize() and your "X" to endSession(). See Lifecycle.

Where to go next