G Growreplies docs

Embed the widget

Install snippet

The widget is a single <script> tag. Drop it on any page, pass an agent ID, and you're live. The bundle is ≤50KB gzipped, runs in a Shadow DOM so it can't be styled by your site, and never blocks page load (it's async).

The snippet

Paste this just before </body>:

<script
    src="https://your-app.test/widget/widget.js?v=ab12cd34"
    data-agent-id="01HXY..."
    async></script>

The full snippet (with your agent ID and the current cache-bust hash) is on every agent's Settings page next to a copy button.

What's in the URL

  • src — points at /widget/widget.js on your Pitchbar deployment. The ?v=<hash> suffix is the bundle's content hash; it changes whenever the widget is rebuilt, so customers can't get stuck on stale versions cached by a CDN.
  • data-agent-id — the published agent's ULID. The widget's loader reads this attribute and uses it on every /v1/widget/init call.
  • async — non-blocking. The widget appears once the bundle finishes downloading; your page's load metrics are unaffected.

What it injects

On boot, the loader:

  1. Creates a <div> at the bottom of <body> and attaches a Shadow DOM to it.
  2. Renders the launcher (small button) inside the shadow root.
  3. Calls POST /v1/widget/init to get an agent config + JWT + recent history.
  4. Wires up trigger listeners (scroll, idle, exit-intent) per the agent's behavior rules.
  5. Persists a small anon_id in localStorage so the same browser keeps the same conversation across reloads.

Customizing the launcher

Theme is fully agent-driven — see Persona, theme & prompts. The widget reads theme.position, theme.primary, theme.accent, theme.radius, and theme.launcher_label from the init response and renders accordingly.

There's no per-page customization — the launcher always reads from the agent. If you need a different look on different pages, embed two different agents.

Programmatic control

The widget exposes a single global, window.Pitchbar.mount(), used by the loader to boot. The script tag's async attribute and auto-mount handle the common case for you, so you don't typically call this directly.

// Mount the widget into a custom host element (rare).
window.Pitchbar.mount(document.getElementById('chat-host'));

There's no public open / close / send / on API yet — those are deferred. If you need to fire conversion analytics on lead capture today, subscribe to the lead.captured webhook instead (see Outgoing webhooks).

Single-page apps

The widget loads once per page-load, but the conversation persists across in-app navigations as long as the script tag stays in the DOM. You don't need to re-mount it when your router changes routes — the Shadow DOM and JWT survive.

If your SPA fully re-mounts on route changes (e.g. you tear down the body), the widget will re-init and resume the visitor's conversation from the last 24 hours of history.

What gets sent on every init

A POST /v1/widget/init includes:

  • agent_id — the value from data-agent-id.
  • page_url — the current location.href, used for the "current page" boost in retrieval.
  • anon_id — the visitor's persistent ID from localStorage, generated on first visit.

The server also reads the Origin, Referer, and Accept-Language headers — Origin for the allowed-origin check, Accept-Language for default language detection.

Versioning & caching

The bundle URL is content-hashed (?v=<hash>). On every new deploy, the hash mutates, so Cache-Control: max-age headers on the bundle can be aggressive (one year) without trapping customers on an old version.

Customers shouldn't manually pin the hash — always copy the snippet fresh from the agent settings page when re-installing.