Skip to main content
The hosted website widget connects visitors to a saved PyAI Agent without putting an API key in your site or requiring you to run a token server.

Publish

  1. Open Agents, choose an Agent, then open Website & phone.
  2. Add every exact website origin that may host the widget, for example https://www.example.com. Production origins must use HTTPS.
  3. Choose the launcher, position, colors, labels, and branding.
  4. Publish and copy the generated snippet:
The widget id is an opaque public identifier. It is not an organization, project, Agent, or key id.

Security model

On page load, v11 resolves only safe display configuration. When a visitor starts a call, PyAI checks the exact browser Origin, the widget and Agent status, account credit, and daily widget/IP limits. It then mints one short-lived key with only omni:session, concurrency 1, the visitor origin, and the Agent as its session label. The browser never receives a long-lived credential. If recording is enabled on the Agent, the widget shows the Agent’s consent notice before requesting microphone permission.

Runtime errors

Listen for pyai:widget-error. Its detail is a stable, sanitized object:
Supported codes are mic_permission_denied, unsupported_browser, origin_not_allowed, credit_exhausted, daily_cap_exceeded, session_unavailable, websocket_failed, config_unavailable, and consent_required. Error events never include session tokens, internal identifiers, upstream responses, or visitor addresses. Listen for pyai:widget:transcript to receive the same sanitized transcript rows shown in the widget. The current public capability is capabilities.transcript: "caller": production emits caller text only, so the widget never fabricates Agent text from audio. detail.version is 1; its fields are role, text, and final. Partial caller updates replace one open row and freeze once at the response boundary. A future runtime can advertise a broader transcript capability without changing this versioned event envelope. Listen for pyai:widget:state to receive semantic listening, thinking, and agent_speaking transitions derived from session controls and response audio. Neither event nor the UI exposes raw frames, tool arguments, tokens, or identifiers. For a strict Content Security Policy, allow the versioned script from https://cdn.pyai.com, API requests to https://api.pyai.com, and voice WebSockets to wss://api.pyai.com. The runtime does not evaluate strings as code or inject customer-provided HTML. If your policy requires nonces for inline styles, put a nonce on the script element; v11 copies it to its style element.

Opening audio and older embeds

Widget v11 keeps the consent line and turn-0 greeting isolated from browser speaker echo. It buffers server PCM until the playback graph is running, sends real-time digital silence instead of microphone/self-audio through the opening drain, and restores normal barge-in on later replies. Versioned widget URLs are immutable. Existing v1-v10 embeds do not update in place. Replace their script URL with /widget/v11/pyai-widget.js; v11 is the certified, published widget (since 2026-08-24). v11 retains protected opening and browser-valid closes, and narrowly normalizes the deployed playback-position advisory while older engine revisions drain. If you operate your own session broker or media UI, follow the browser voice agent guide and mint a short-lived, origin-locked session token with POST /v1/omni/sessions. Never put a secret key in browser code. The hosted widget supports the same safe declarative launcher variants and URL, telephone, and event actions. It never evaluates custom JavaScript or HTML.