Embedding the widget
The Voxy widget lets your website visitors start a voice call or text chat with one of your AI agents. It ships as a single<script> tag — the runtime mounts itself, fetches its configuration from the API, and validates the current origin against your allowlist before showing anything on the page.
The embed snippet
Paste this just before the closing </body> tag (or inside <head> with async). The only required attribute is data-widget-id — every other setting (colors, copy, allowed channels) lives in your widget config and is loaded at runtime.
<script
src="https://voxyhq.com/widget/v1.js"
data-widget-id="01ABCDE..."
async
></script>Single-page apps that render outside of a normal HTML page can mount the widget by injecting the same tag programmatically:
// Drop this in your app shell (Next.js layout, React entry, etc.)
import { useEffect } from 'react';
export function VoxyWidget({ widgetId }: { widgetId: string }) {
useEffect(() => {
if (document.querySelector('script[data-voxy-widget-loaded]')) return;
const s = document.createElement('script');
s.src = 'https://voxyhq.com/widget/v1.js';
s.async = true;
s.dataset.widgetId = widgetId;
s.dataset.voxyWidgetLoaded = '1';
document.body.appendChild(s);
}, [widgetId]);
return null;
}Allowed origins
Each widget has an allowedOrigins list. The runtime calls GET /v1/public/widgets/:id/config at boot — the server checks the request Origin header against that list and returns 403 if it doesn't match. The widget logs a clear error to the browser console and quietly refuses to mount.
Add every domain that should be allowed to show the widget (including staging) in the workspace dashboard under Web widget → Allowed origins. Include the scheme, host, and port — no trailing path. For example https://www.example.com or http://localhost:3000.
Content Security Policy
If your site sends a Content-Security-Policy header, add the directives below so the browser will let the widget load, open its API connections, and play call audio.
script-src https://voxyhq.com
connect-src https://voxyhq.com wss://voxyhq.com https://*.voxyhq.com wss://*.voxyhq.com
media-src https://*.voxyhq.com blob:
img-src https://voxyhq.com data:
style-src 'unsafe-inline' # widget injects styles into its Shadow DOMA minimal complete CSP header looks like this:
Content-Security-Policy: default-src 'self';
script-src 'self' https://voxyhq.com;
connect-src 'self' https://voxyhq.com wss://voxyhq.com https://*.voxyhq.com wss://*.voxyhq.com;
media-src 'self' https://*.voxyhq.com blob:;
img-src 'self' https://voxyhq.com data:;
style-src 'self' 'unsafe-inline';
frame-ancestors 'self';We deliberately do not publish an integrity="sha384-…" hash for v1.js. We ship security and reliability updates frequently; pinning an integrity hash would break every embedded site the moment we publish a new build. The runtime is served from our own CDN over HTTPS with strict transport security. If your compliance program requires SRI, host a pinned copy of the script yourself and update it on your own cadence.
Style isolation
The widget mounts under a Shadow DOM root, so your site's CSS cannot accidentally style the widget and the widget's CSS cannot leak into your page. The host element on your page is a single <div data-voxy-widget> appended to <body>. You don't need to add any extra wrapper.
Privacy & visitor IDs
On first interaction the widget generates a 26-character ULID and stores it in localStorage under voxy_widget_visitor_id. It is used solely for per-visitor rate limiting and to stitch repeat sessions together in your inbox. No fingerprinting, no tracking pixels, no third-party beacons. The visitor's name and email are only collected through the pre-chat form and are validated on the server.
Versioning & updates
The script URL is versioned (/widget/v1.js). Backwards- compatible improvements ship to v1 automatically. Breaking changes get a new path (v2) and we'll publish a migration note in the changelog well in advance.