Skip to content

Embed snippet

The complete embed is two pieces of HTML:

<powsoo-voice widget-id="wid_YOUR_ID_HERE"></powsoo-voice>
<script>
(() => {
const d = document, s = d.createElement('script');
s.type = 'module';
s.src = 'https://w.powsoo.com/widget.js';
d.head.append(s);
})();
</script>

Place these anywhere in <body>. That is the entire integration.

How it loads

  1. The IIFE creates a <script type="module"> tag pointing to widget.js on Powsoo’s CDN (Cloudflare R2 + CDN).
  2. widget.js registers the <powsoo-voice> custom element — a Lit web component with shadow DOM.
  3. On connectedCallback the element reads widget-id, fetches its config from the API, and renders a call button.
  4. When the user clicks, the widget mints a voice token and starts the Retell/Vapi WebRTC call.

Widget config (theme, mode, agent metadata) is fetched once on first load and cached client-side for ~30 minutes. Subsequent page loads within that window skip the config fetch.

Attributes

AttributeRequiredTypeDescription
widget-idstringThe wid_* identifier from the Powsoo dashboard. Public — contains no credentials.

Placement and positioning

The <powsoo-voice> element renders inline by default. To pin it to a corner of the screen:

<powsoo-voice
widget-id="wid_abc123"
style="position: fixed; bottom: 24px; right: 24px; z-index: 999;"
></powsoo-voice>

The widget’s shadow DOM fully isolates its styles from the host page — host page CSS cannot bleed in and widget CSS cannot bleed out.

Security model

widget-id is public — safe to commit to source, include in client HTML, and deploy to a CDN. It contains no credentials.

Access control is server-side: the Powsoo API validates the Origin header on every request against the widget’s Allowed origins list, configured in the dashboard. A mismatch is rejected before any token is minted. See Security for the full model.

Next: Framework guides

See Framework guides for placement instructions for React, Next.js, Vue, Webflow, WordPress, and others.