Skip to main content
Headless mode keeps Puntego’s authenticated matching, grounding, confirmation, postconditions, and audit trail, while your product renders the Guide transcript and Experience content. It is intended for teams that need complete visual control—not for bypassing Puntego’s execution policy.
Release preview: The headless APIs below are implemented and tested in the repository, but the referenced @puntego/embed and @puntego/react packages are not yet published to npm. Treat these examples as the upcoming package contract until the package release is announced. This does not affect the production hosted-script path for Puntego’s built-in Guide and Experiences.

Framework-neutral client

Load the page-level runtime once, explicitly in headless mode, then create the state adapter:
The adapter is safe to import and instantiate during server rendering. Commands fail clearly until a browser runtime is ready. destroy() detaches adapter listeners; it does not destroy the shared page-level Puntego runtime. To tell the guide about the signed-in account, hand the adapter plain data whenever it changes. It keeps a JSON copy and serves it on every message:
Page-reported facts ground answers and never authorize anything. When accountId (or the signed token’s subject) names another account, the guide starts a new conversation: once setContext resolves, the Guide and turn snapshots no longer hold the previous account’s messages, and their subscribers are notified. setContext(null) stops sending account facts. See Account facts. The adapter also carries assist, the guide as a tool for other AI agents (query, handleToolCall, tools), when the owner turned it on. A headless install renders no guide card, so only ask is available there: showing the way and actions the visitor approves need the guide’s own surfaces. See Other AI agents.

Typed turns

getGuideSnapshot() keeps its four fields for existing integrations. For a complete custom answer surface, read the typed turn model instead: each PuntegoTurn carries the visitor’s question, the answer text, the disclosed sources, the follow-up chips, a tour proposal, the footer actions the built-in guide would show, and the turn’s status and timings.
getTurnSnapshot() returns the last 20 turns, oldest first, deep-frozen. The client re-reads it once per burst of turn:* events and returns the same reference until the next change, so it can back useSyncExternalStore directly. A turn never carries credentials, proofs, raw source identities, selectors, target ids, or executable actions: web sources expose a navigable href, published notes expose only their title and revision, and generative UI blocks are reduced to their id, kind, title, and visible text. A chart block also carries chart (its figures with their layout as fractions of the plot) and a metric grid metrics (each tile’s limit, meter, change and trend), so you can draw them yourself; see React components. Footer actions are ordered tertiary → neutral → primary with at most one primary. Their labels are localized in the conversation’s language (the page’s language until the first answer says otherwise). footer.meta includes aiLabel, the visible AI identification your answer surface should show. It is empty on an error turn that shows no generated text and on a notice the Worker writes itself (the site’s pause or a limit), because neither is an AI answer. runTurnAction(turnId, actionId) runs one of the latest turn’s footer actions (show_me_how, do_it, point_it_out, take_me_there, …) from your own button, through the built-in button’s path: it resolves false when the turn does not offer that action now, and do_it/undo need a real visitor gesture, so call it from your click handler. React apps can render the whole card with their own components through @puntego/react/ui. Commands are inert in privacy-only mode, while consent blocks the assistant, and after destroy(). A runtime older than the turn model has no turn commands: the client then returns an empty snapshot, rateTurn, respondToProposal and replayTour resolve false, and the other commands do nothing. replayTour also resolves false on a hosted runtime that predates it.

React hooks

Pass headless to the page-level provider and render snapshots with normal React components:
Both hooks use external-store subscriptions and provide stable server snapshots, so they can render in React and Next.js hydration paths. The provider still boots only in a client effect. usePuntegoGuide() also returns the typed turn model — turn (the latest turn, or null), turns, and the commands selectFollowUp, respondToProposal, replayTour, rate, resolve, retry, and stop:

Experience engagements

getExperienceSnapshot() and usePuntegoExperiences() expose deployed content, revision identity, matched route, and checklist state. They accept only typed engagements:
  • impression or dismissal;
  • an authored action index;
  • checklist completion;
  • validated Survey or Form answers.
The browser SDK never accepts a deployment id, revision id, environment, visitor identity, callback, URL, credential, or signed match receipt from custom UI code. It reconstructs the authorized engagement from the current matched snapshot and sends the private proof internally. A stale route, ambiguous id, invalid payload, expired receipt, or non-headless runtime fails closed. An accepted authored start_playbook action can start only the playbook bound to the server receipt. A tour with no playbook carries a start_tour action: it starts the tour’s authored steps and nothing else. An authored Guide prompt can start a text stream without reopening the built-in panel. Arbitrary code and arbitrary network execution are not part of the API. Snapshots preserve the presentation discriminants for hint, release_feed, and progress. Custom UI owns the local open/collapse state, while the same typed engagement methods remain authoritative. See Hints, release feeds, and progress badges for the content shapes and event model.

Reusable placement Collections

Resolve a stable inline placement without exposing the private match proof:
The server returns at most the first eligible production Experience, never the IDs of failed fallback candidates. The public snapshot includes the stable key, typed surface, Collection version, immutable revision identity, and allowed checklist state. App identity, audience evaluation, environment selection, and the signed receipt remain internal. See Experience Collections for native card/banner mounting and lifecycle behavior. Headless mode disables the built-in launcher, chat panel, automatic opening, automatic questions, proactive nudges, voice hotkey, and built-in Experience renderer. It does not disable required consent/privacy controls, PII scrubbing, target policy, risk confirmation, action postconditions, or audit events. Keep these boundaries in your custom UI:
  • Mint authenticated customer identity tokens on your server and pass each one-time token to the first loader/provider mount. Never persist it.
  • Render status and errors with aria-live; keep controls keyboard accessible and restore focus when custom dialogs close.
  • Show a clear confirmation for consequential action requests and a durable success/failure state. Runtime policy remains authoritative.
  • Call trackEvent(name) only for bounded, authored milestone names. Arbitrary event payloads are intentionally unsupported. CRM user/group traits must be supplied by your authenticated server through the one-time identity handoff. Account facts (plan, seats, usage, features) go through setContext: facts the page reports only ground answers, and anything that needs proof must be signed by your server (signedAccountFacts).

Auditability

Guide streams continue to emit typed runtime events. Experience engagement responses include only public acceptance, duplicate, action receipt, and submission receipt identifiers. The runtime’s signed matching proof never appears in a snapshot or event. Each turn emits turn:start once, turn:status per stage change, turn:text per released text batch, coalesced turn:update for anything else, and exactly one of turn:complete, turn:stopped, or turn:error (a turn replaced or cleared before it ended gets turn:stopped). turn:removed fires once when a turn leaves the conversation (a new conversation, an account switch), so a UI that caches turns drops it. turn:resolved, feedback:submitted, panel:open, panel:close, and navigate (fired just before an in-product navigation) complete the set. The chat:* events keep firing unchanged. Use experience:engaged, guide-action:proposed, guide-action:started, guide-action:completed, and guide-action:failed for host-side observability. The authoritative action and Experience audit records remain on Puntego’s authenticated backend.