Back to docs
Chat widget

Embed the widget.

One snippet, anywhere. The widget loads asynchronously, queues calls until the script is ready, and locks itself to the origins you configured in the dashboard.

The snippet

Paste this just before </body> on any page where the widget should appear. Replace apiKey and widgetId with the values shown in your dashboard after you create a widget.

<!-- Reva Chat Widget -->
<script>
  (function(w,d,s,o,f,js,fjs){
    w['RevaWidget']=o;w[o] = w[o] || function () { (w[o].q = w[o].q || []).push(arguments) };
    var link = d.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'https://reva.reasonvoice.ai/style.css';
    d.head.appendChild(link);
    js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
    js.id = o; js.src = f; js.async = 1; fjs.parentNode.insertBefore(js, fjs);
  }(window, document, 'script', 'reva', 'https://reva.reasonvoice.ai/reasonvoice-widget.js'));

  reva('init', {
    apiKey: 'reva_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    widgetId: 'widget_xxxxxxxxxxxx',
    apiUrl: 'https://api.reasonvoice.ai',
    theme: {
      primaryColor: '#0A0A0F',
      accentColor: '#B0286E',
      position: 'bottom-right',
      borderRadius: '14px',
    },
    welcomeMessage: 'Hi! How can we help?',
  });
</script>

The exact snippet — pre-filled with your real keys and theme — is available on each widget's page in the dashboard under Get embed code.

What it loads

  • https://reva.reasonvoice.ai/style.css — widget styles (~6 KB gzipped)
  • https://reva.reasonvoice.ai/reasonvoice-widget.js — the widget runtime

Both are served from our edge CDN. The script is loaded with async so it never blocks rendering. Calls to reva(...) made before the script finishes are queued and flushed once it loads.

Are these keys safe to put in client code?

Yes. apiKey values prefixed with reva_ are publishable — same model Stripe uses for pk_live_* or Google Maps for browser keys. They're designed to ship in your HTML.

The server enforces an allow-list of origins per key. A widget with an https://example.com allow-list won't respond to requests with any other Origin header, regardless of who has the key.

Your secret server-side keys (sk_live_*) are different — those should never appear in client code. The dashboard never shows them inside an embed snippet.

Theme

Every key has a sensible default; pass only the ones you want to override.

KeyTypeDefaultNotes
primaryColorstring#4F46E5Header + send button
secondaryColorstring#E5E7EBBot message background
accentColorstring#10B981Active state, focus ring
textColorstring#1F2937Body text
backgroundColorstring#FFFFFFPanel background
positionstringbottom-right"bottom-right" | "bottom-left"
sizestringmedium"small" | "medium" | "large"
borderRadiusstring12pxAny CSS length
fontFamilystringInter, sans-serifAny CSS font stack
logoUrlstring""Custom logo on the panel header
showLogobooleanfalseRender logoUrl when true

Features

KeyTypeDefaultNotes
typingIndicatorbooleantrue"…" while the agent is thinking
messageHistorybooleantruePersist messages across page loads
userFeedbackbooleantrue👍/👎 on each agent reply
streamingbooleantrueToken-by-token rendering via SSE
fileUploadbooleanfalseImage/document attachments
hideBrandingbooleanfalseHide "Powered by ReasonVoice" footer

hideBranding is on by default for paid plans only. It removes the small “Powered by ReasonVoice” line at the foot of the panel.

Welcome + placeholder

welcomeMessage is the first message shown when a visitor opens the panel for the first time. placeholderText is the empty-state hint inside the input box. Both accept any string; we escape them safely so quotes and emoji are fine.

Verify it’s working

  1. Load the page on a domain that's in your widget's allow-list.
  2. Open DevTools → Console. You should see no errors and a successful GET /api/widgets/{id}/public-config.
  3. Click the launcher and send a test message.

If the panel opens but messages don't send, the most common cause is the page's origin not being in the widget's allow-list. Add it in the dashboard under Widget settings → Allowed origins.

Removing the widget

To remove the widget from a page, delete the snippet. There's no global cleanup function to call — the widget mounts once on script load and unmounts when the page unloads.

Next

Voice agents, voice cloning, and the full API reference are coming soon. In the meantime, write to the founder if you have a question we haven't answered. Get in touch.