Install the AI agent

One script line, pasted once. Penzago ships every improvement to your agent automatically — you never touch the line again.

The line

Your Penzago contact gives you a widget key (agent_…). Paste this anywhere in your page — it floats a chat button in a corner:

<script async src="https://www.penzago.com/agent.js" data-reflow-agent="agent_YOURKEY"></script>

Or embed the agent as a card in your layout. With data-mode="inline" the card renders exactly where the line sits, so paste it at the spot in the page the card should occupy:

<script async src="https://www.penzago.com/agent.js" data-reflow-agent="agent_YOURKEY" data-mode="inline"></script>
AttributeRequiredWhat it does
data-reflow-agentyesYour widget key. Without it, nothing renders.
data-modenoinline for the in-page card. Anything else (or nothing) is the floating launcher.
data-positionnoleft or right — pins the launcher to that corner, overriding the side configured in Penzago.

How updates reach you

The pasted line is a tiny loader with a stable URL. On each page load it reads a short-lived manifest and loads the current agent runtime — a content-hashed, immutably-cached file. When we ship an improvement we publish a new hash; your pages pick it up within minutes, and the line you pasted never changes. If anything in that chain ever fails, the widget simply does not appear — your page is never affected.

Before it will render

Your widget answers only on domains you have approved — that is a feature, not a hurdle: your key is visible in your page source, and the allowlist is what makes it useless to anyone who copies it onto another site. The match is exact, so https://www.example.com and https://example.com are separate entries — give your Penzago contact every hostname you embed on, staging included. On a domain that is not on the list, the widget renders nothing and your page stays untouched.

React, Vue and other SPAs

If your framework re-renders the DOM, load the runtime with a bare line (no key) and place the element yourself, wherever your component tree wants it:

<script async src="https://www.penzago.com/agent.js"></script>

<reflow-agent agent-key="agent_YOURKEY" mode="inline"></reflow-agent>

<reflow-agent> is a standard custom element: render it from JSX/templates like any tag. Attributes are read once when the element enters the page — to change one, remove the element and re-add it. A remount restores the visitor's conversation automatically.

Styling the inline card

The inline card inherits your page's font by default and exposes a small set of CSS custom properties — set them on the element and leave the rest alone:

reflow-agent {
  --rfa-primary: #0f5132;
  --rfa-bg: #fbfaf7;
  --rfa-ink: #1b1b1b;
  --rfa-radius: 4px;
}

Your values outrank the office theme, which outranks the built-in defaults. The floating launcher ignores host styling entirely — it is the business's presence and looks the same on every page. Class names inside the widget's shadow root are private and change without notice; the custom-property surface is the only stable styling API.

Content-Security-Policy

If your site ships a CSP, add these to your existing policy:

DirectiveAddWhy
script-srchttps://www.penzago.comthe pasted agent.js and the versioned runtime it injects
connect-srchttps://www.penzago.commanifest, config, chat stream and analytics beacon — every request the widget makes
style-src'unsafe-inline'the widget's own stylesheet, injected inside its shadow root

A complete policy for a fully locked-down page — our test suite runs the widget under exactly this policy on every build, so it cannot drift from reality:

default-src 'none'; script-src 'self' https://www.penzago.com; connect-src https://www.penzago.com; style-src 'unsafe-inline'; img-src 'self'; base-uri 'none'

For the reviewer's checklist: frame-ancestors is not involved — the widget is a custom element in your own DOM; it neither frames your page nor itself. No font-src entry is needed — themes use font stacks, never webfont URLs. img-src only comes up when your agent is configured with a logo, which is served from Penzago's storage domain; if you use one, we'll give you the exact host to allow.

If your policy uses 'strict-dynamic', host allowlists are ignored by design — put your nonce on the pasted <script> tag instead (nonce="…" next to async). The loader injects the agent runtime as a non-parser-inserted script, so under 'strict-dynamic' it inherits the pasted tag's trust automatically; connect-src still needs the entry above.