Drop-in Widget — pouchy.ai/embed

The zero-code way to put a Pouchy companion in any page: one <iframe> renders a complete chat UI (bubbles, composer, streaming replies), authenticated by the same session token your backend already mints (POST /v1/sessions) or a user's PAT. When you outgrow the canned UI, everything here is available headless via @pouchy_ai/companion-sdk — the widget is built on it.

Embed

<iframe
  src="https://pouchy.ai/embed?token=SESSION_TOKEN&theme=dark&accent=%23ff6b81"
  style="width: 380px; height: 560px; border: 0; border-radius: 16px"
  allow="clipboard-write"
></iframe>

Query params

Param Values Effect
token pchy_… Session token or PAT. Optional — can be delivered via pouchy:init instead (below).
theme dark Dark palette. Default light.
accent %23rrggbb (URL-encoded hex) Recolors the send button + user bubbles. Text ink is auto-picked by luminance so any accent stays readable.

postMessage protocol

For parents that prefer not to put the token in a URL, and to drive the widget live. Every message the widget posts carries source: 'pouchy-widget'.

Widget → parent:

Type Payload When
pouchy:loaded The page booted (safe moment to send pouchy:init).
pouchy:ready { session } Connected; the companion is live.
pouchy:message { text } An assistant reply landed.
pouchy:sent { text } A user message left the composer (typed or via pouchy:send).
pouchy:tool_call { id, name, args } The companion called an app tool (declare tools via the SDK for full round-trips).
pouchy:error { code?, message } Connect/turn error.

Parent → widget:

Type Payload Effect
pouchy:init { token, surface? } Connect with this token (alternative to ?token=).
pouchy:send { text } Send a user message programmatically.
pouchy:context { event } Push a world-state event (same shape as SDK sendWorldState).
const frame = document.querySelector('iframe');
window.addEventListener('message', (ev) => {
  if (ev.data?.source !== 'pouchy-widget') return;
  if (ev.data.type === 'pouchy:loaded')
    frame.contentWindow.postMessage({ type: 'pouchy:init', token: SESSION_TOKEN }, 'https://pouchy.ai');
  if (ev.data.type === 'pouchy:message') console.log('companion said:', ev.data.text);
});

Security model

The embedding page is the trust anchor. The widget pins the embedder's origin (learned from the referrer at load, or adopted from the first parent message when the referrer is stripped) and afterwards only accepts control messages from that origin and only posts session/tool payloads back to it — never to '*', never to sibling frames or popups (ev.source !== window.parent is rejected outright). Treat session tokens like the credentials they are: mint them short-lived per user from your backend.

Known limits (v1)

  • Widget chrome is English (the conversation itself follows the agent/user language). A lang param is a planned follow-up.
  • Text modality only — for voice, use the SDK's connectCall in your own UI.