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.
| Route | You need | Time |
|---|---|---|
| A. Website widget | a pk_ publishable key | ~5 minutes |
| B. Backend catalog sync | a one-time pair code (or your pk_) | ~30 minutes |
| C. Mobile app | a vb_pk_ publishable key (or your server) | ~30 minutes |
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?
- React —
npm install @monoverse/voicebot-react - Vue —
npm install @monoverse/voicebot-vue - Next.js —
npm 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:
- Pair — exchange a one-time
VB-XXXX-XXXXcode from the cabinet (or yourpk_viapair-by-key) for a per-tenant HMAC shared secret, stored encrypted. - Full snapshot — stream every product, category, and page as gzipped NDJSON.
- Deltas — push small signed batches as your data changes, on your own schedule.
Pick your stack:
- Node.js —
npm install @monoverse/voicebot-node - Python —
monoverse-voicebot(install from source; PyPI release in progress) - PHP / Laravel —
composer require monoverse/voicebot-laravel-sync - Any other backend — implement the signed ingest protocol directly
- WordPress / WooCommerce, or no code at all — the plugin or the managed crawler
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.
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
- React Native
- Flutter
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
{ "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).
flutter pub add voicebot_flutter
Add a microphone usage string to ios/Runner/Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>Talk to the in-app assistant.</string>
Android — the package declares RECORD_AUDIO and the SDK requests it at runtime for you.
Uses a native plugin, so not Expo Go — use a development build.
C3. Init the client
Init is synchronous and does no network — it just configures the client.
- React Native
- Flutter
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' },
});
import 'package:voicebot_flutter/voicebot_flutter.dart';
final client = VoicebotClient.init(
baseUrl: 'https://api.monoverse.tech',
tokenProvider: PublishableKeyTokenProvider(
issueTokenUrl: Uri.parse('https://api.monoverse.tech/api/v1/mobile/issue-token-public'),
publishableKey: 'vb_pk_…',
appId: 'com.yourco.app',
poster: myHttpPoster, // inject your HTTP client (keeps the SDK dependency-free)
),
);
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.
- React Native
- Flutter
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)}`);
client.registerToolHandler('add_to_cart', (args) async {
await myCart.add(args['id']);
return {'success': true, 'cartItemCount': myCart.count};
});
client.registerDeepLinkMap('open_product', (args) => 'myapp://product/${args['id']}');
C5. Start a session and listen
This is the launcher action — wire it to your "open assistant" button.
- React Native
- Flutter
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?');
// Text-only surface (no native audio engine at all):
final session = client.startTextSession(lang: 'en'); // any supported language code
session.transcripts.listen((t) => print('${t.role.name}: ${t.text}'));
session.status.listen((s) => print(s.name));
session.errors.listen((e) => print('error: ${e.message}'));
session.deepLinks.listen((d) => router.go(d.uri)); // route navigations
// 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.
- React Native
- Flutter
// 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
// startVoiceSession() requests the mic permission for you before capture:
final session = client.startVoiceSession(config: const SessionConfig(lang: 'en'));
await 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() 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
- API keys — which credential goes where, with the real formats.
- Auth & API keys (mobile) — move from a publishable key to server-minted tokens.
- Custom site integration — cart/variant actions on a custom storefront.
- Tool handlers & deep links — the full action map.
- Privacy & store compliance — before you submit to the app stores.