Skip to content

Option reference

The table of every option, generated from the schemas; authoritative.

Last updated

The canonical surface lives in src/schema/widget.ts and src/schema/deployment.ts as Zod schemas. Every field carries a .describe(...) string and (where useful) .meta({ examples }) — this page is a flat reference auto-generated from those annotations.

When the schema and this doc disagree, the schema wins. Run bun run gen:docs after editing a schema to refresh the tables; CI fails if this file is out of sync.

For longer-form prose on what each subsystem does, see the features/ directory:

  • Theming
  • Sizing
  • Launcher
  • Header actions
  • Composer
  • Feedback
  • i18n
  • Modes

Conventions#

  • Every field is optional unless its row says otherwise.
  • "Default" is what the runtime applies when the field is absent. — means there's no schema-level default; the resolver falls through to runtime behaviour or leaves the field undefined.
  • Field names use the schema (camelCase) form. The web-component + <script data-*> boot accept the kebab-case equivalent (e.g. data-launcher-variant ↔ launcher.variant).
  • "Server-pushable" fields can arrive in HandshakeResponse.config; see § Server settings.

<!-- AUTO-GENERATED:CONFIG-REFERENCE START -->

<!-- This block is generated by scripts/gen-config-docs.ts from the Zod schemas in src/schema/widget.ts + src/schema/deployment.ts. Edit those schemas (.describe(...) + .meta({ examples })) and run bun run gen:docs to regenerate. CI fails when this block drifts. -->

Connection (ConnectionConfig)#

Where the widget talks to and how it authenticates. Owned by the embedding developer (typically baked into the page template / env var) — distinct from userPrefs, which is the dashboard-pushable UX surface.

FieldTypeDefaultDescription
widgetIdstring"default"Stable per-instance id used to namespace localStorage keys. Set this when mounting more than one widget on the same page so their conversation state doesn't collide.
publicKey?string—Deployment public key (agent_pk_…) issued from the dashboard. Sent on every API call as the x-public-key header when present.
aiAgentDeploymentIdstring—Required deployment UUID from the dashboard. Picks which agent / settings bundle answers this mount. Sent as x-ai-agent-deployment-id.
baseUrl?string—MAIN site origin (e.g. https://help.ai) — the API modules derive from it: agent API at ${baseUrl}/api/agent, data API at ${baseUrl}/api/data. Set this to point at your staging / self-hosted / custom backend. Default: the brand's defaultBaseUrl baked at build time.
agentApiBaseUrl?string—Full agent-API base — set it to reach a STANDALONE module-agent-api directly (local module on its own port, e.g. http://localhost:3069; routes attach as-is: /pai/handshake). Precedence: this option > derived from the main origin (${baseUrl}/api/agent). Not server-pushable.
dataApiBaseUrl?string—Full data-API base (e.g. https://help.ai/api/data, or a standalone local module like http://localhost:3071) — the service serving content, form definitions (GET /pai/bootstrap), and form submissions at root-level paths. Precedence: this option > server-pushed config.dataApiBaseUrl > derived from the main origin (${baseUrl}/api/data).
userContext?object—Host-asserted end-user context (informational, untrusted — not authentication). Recognised id / email / name / plan / role / phone plus any extra custom scalar fields forwarded to the agent.
pageContext?object—Host-asserted page/visit context (informational, untrusted). Where the visitor is + the surface (area) they're using, plus any custom scalar fields, forwarded to the agent.
endpoints?object—HTTP endpoint overrides. upload / transcribe paths are appended to the agent base (${baseUrl}/api/agent), submitForm to the data base (dataApiBaseUrl); absolute URLs are accepted too.
debug?boolean | "off" | "error" | "warn" | "info" | "debug" | "trace"—Console log verbosity. false / "off" silences everything (default in production). true → "debug". Levels error / warn / info / debug / trace gate output additively. Use "trace" to capture per-chunk SSE frames + every state transition. Also toggleable at runtime via chat.setDebug(level) or ?<brand>-debug=trace in the page URL — the URL beats this option, this option beats the persisted localStorage value.

Examples:

json
[
  {
    "publicKey": "agent_pk_demo",
    "aiAgentDeploymentId": "01900000-0000-7000-8000-000000000001"
  },
  {
    "publicKey": "agent_pk_demo",
    "aiAgentDeploymentId": "01900000-0000-7000-8000-000000000001",
    "widgetId": "support"
  },
  {
    "publicKey": "agent_pk_demo",
    "aiAgentDeploymentId": "01900000-0000-7000-8000-000000000001",
    "baseUrl": "https://api.acme.com"
  }
]

Deployment widget settings (WidgetSettings)#

Portal-configurable widget settings — thirteen sections, one per dashboard tab. The backend pushes this shape on /pai/handshake under config, except forms, which the data API serves separately.

FieldTypeDefaultDescription
presentation?object—Layout fundamentals — mode, anchor corner, iframe isolation, panel size + drawer inset.
behavior?object—Conversation behaviour — lifecycle, streaming, and the transcript surface (scroll, feedback).
theme"auto" | "light" | "dark" | "host" | object"auto"Theme preference (auto follows OS) or a tokens override object (accent, radius, fontFamily).
launcher?object—Floating-launcher (FAB) appearance and behaviour. The FAB is the round button in the corner that opens the chat panel. Floating mode only.
header?object—Panel-header configuration.
composer?object—Composer chrome — send-button styling + attachment limits.
feedback?object—Audible + tactile feedback.
i18n?object—Locale + i18n overrides.
footer?object—Footer — attribution + composer disclaimer.
features?object—Capability flags — file upload, voice mode, HITL, and operator requests.
forms?object[]—Event-driven forms — an ordered list (order = priority). Each binds to trigger event(s), shows in the conversation (a blocking one locks the message box until submit/skip), dedupes by frequency, and records via endpoints.submitForm. Generic across verticals.
modules?object[]—Messenger tabs — an ordered list of up to 4 tabs, each picking a layout + the content collections it shows (contentCollectionIds) + a translatable label. One tab → no tab bar (just that content); zero → an empty state. Floating / drawer / modal only.
tracking?object—Anonymous usage tracking for the widget owner — GA-style pixel events (open, send, form submit, …) with page / device / locale dimensions. Never includes message or form content; the only visitor identifier is the widget's anonymous visitorId.

Examples:

json
[
  {
    "presentation": {
      "mode": "floating",
      "position": "bottom-right"
    },
    "theme": "auto"
  },
  {
    "presentation": {
      "mode": "floating"
    },
    "theme": {
      "accent": "#7c3aed",
      "radius": "12px"
    },
    "launcher": {
      "variant": "pill",
      "label": "Ask AI",
      "callout": {
        "text": "👋 Need help?"
      }
    }
  },
  {
    "presentation": {
      "mode": "inline"
    },
    "behavior": {
      "responseMode": "streaming"
    },
    "features": {
      "voice": "local"
    }
  }
]

Full widget config (WidgetConfig)#

Full tooling config — connection (where + auth) + widget settings (look + behaviour) + page-mode handshake extras. The production widget does not import this Zod schema. Mode-specific constraints (e.g. position only meaningful in floating) are NOT enforced here so the same object can flow across mode changes.

FieldTypeDefaultDescription
widgetIdstring"default"Stable per-instance id used to namespace localStorage keys. Set this when mounting more than one widget on the same page so their conversation state doesn't collide.
publicKey?string—Deployment public key (agent_pk_…) issued from the dashboard. Sent on every API call as the x-public-key header when present.
aiAgentDeploymentIdstring—Required deployment UUID from the dashboard. Picks which agent / settings bundle answers this mount. Sent as x-ai-agent-deployment-id.
baseUrl?string—MAIN site origin (e.g. https://help.ai) — the API modules derive from it: agent API at ${baseUrl}/api/agent, data API at ${baseUrl}/api/data. Set this to point at your staging / self-hosted / custom backend. Default: the brand's defaultBaseUrl baked at build time.
agentApiBaseUrl?string—Full agent-API base — set it to reach a STANDALONE module-agent-api directly (local module on its own port, e.g. http://localhost:3069; routes attach as-is: /pai/handshake). Precedence: this option > derived from the main origin (${baseUrl}/api/agent). Not server-pushable.
dataApiBaseUrl?string—Full data-API base (e.g. https://help.ai/api/data, or a standalone local module like http://localhost:3071) — the service serving content, form definitions (GET /pai/bootstrap), and form submissions at root-level paths. Precedence: this option > server-pushed config.dataApiBaseUrl > derived from the main origin (${baseUrl}/api/data).
userContext?object—Host-asserted end-user context (informational, untrusted — not authentication). Recognised id / email / name / plan / role / phone plus any extra custom scalar fields forwarded to the agent.
pageContext?object—Host-asserted page/visit context (informational, untrusted). Where the visitor is + the surface (area) they're using, plus any custom scalar fields, forwarded to the agent.
endpoints?object—HTTP endpoint overrides. upload / transcribe paths are appended to the agent base (${baseUrl}/api/agent), submitForm to the data base (dataApiBaseUrl); absolute URLs are accepted too.
debug?boolean | "off" | "error" | "warn" | "info" | "debug" | "trace"—Console log verbosity. false / "off" silences everything (default in production). true → "debug". Levels error / warn / info / debug / trace gate output additively. Use "trace" to capture per-chunk SSE frames + every state transition. Also toggleable at runtime via chat.setDebug(level) or ?<brand>-debug=trace in the page URL — the URL beats this option, this option beats the persisted localStorage value.
presentation?object—Layout fundamentals — mode, anchor corner, iframe isolation, panel size + drawer inset.
behavior?object—Conversation behaviour — lifecycle, streaming, and the transcript surface (scroll, feedback).
theme"auto" | "light" | "dark" | "host" | object"auto"Theme preference (auto follows OS) or a tokens override object (accent, radius, fontFamily).
launcher?object—Floating-launcher (FAB) appearance and behaviour. The FAB is the round button in the corner that opens the chat panel. Floating mode only.
header?object—Panel-header configuration.
composer?object—Composer chrome — send-button styling + attachment limits.
feedback?object—Audible + tactile feedback.
i18n?object—Locale + i18n overrides.
footer?object—Footer — attribution + composer disclaimer.
features?object—Capability flags — file upload, voice mode, HITL, and operator requests.
forms?object[]—Event-driven forms — an ordered list (order = priority). Each binds to trigger event(s), shows in the conversation (a blocking one locks the message box until submit/skip), dedupes by frequency, and records via endpoints.submitForm. Generic across verticals.
modules?object[]—Messenger tabs — an ordered list of up to 4 tabs, each picking a layout + the content collections it shows (contentCollectionIds) + a translatable label. One tab → no tab bar (just that content); zero → an empty state. Floating / drawer / modal only.
tracking?object—Anonymous usage tracking for the widget owner — GA-style pixel events (open, send, form submit, …) with page / device / locale dimensions. Never includes message or form content; the only visitor identifier is the widget's anonymous visitorId.
site?——Site config — applies in mode: "page". Comes from the handshake response, not user-edited.
blocks?——Blocks (nav + link cards) — applies in mode: "page". Comes from the handshake response.

Examples:

json
[
  {
    "publicKey": "agent_pk_demo",
    "aiAgentDeploymentId": "01900000-0000-7000-8000-000000000001",
    "presentation": {
      "mode": "floating"
    },
    "theme": "auto"
  },
  {
    "publicKey": "agent_pk_demo",
    "aiAgentDeploymentId": "01900000-0000-7000-8000-000000000001",
    "presentation": {
      "mode": "modal",
      "size": {
        "width": "720px",
        "height": "640px"
      }
    }
  }
]

Handshake response (HandshakeResponse)#

FieldTypeDefaultDescription
deploymentobject—
agentobject—
config?object—
site?object—
blocks?object—
welcome?object—
visitorIdstring—
conversationIdstring—
userPrefs?object—
rebind?object—

<!-- AUTO-GENERATED:CONFIG-REFERENCE END -->


Storage#

OptionTypeDefaultServer-push
storageClientStorageLocalStorageAdapter()no
ts
interface ClientStorage {
  get(key: string): string | null;
  set(key: string, value: string): void;
  remove(key: string): void;
}

Adapter methods are synchronous. Swap the default localStorage adapter for sessionStorage, an encrypted wrapper, or an in-memory shim (tests, SSR). The adapter receives dynamic draft.<conversationId> keys whose values are exact unsent textarea text and owns their storage, retention, and privacy behavior.

storage and the function-typed onMessage / onOpen / onClose / onError / target callbacks are runtime-only and not on the schema (they can't be JSON-serialised).


Callbacks#

OptionType
onMessage(msg: { role, text }) => void
onOpen() => void
onClose() => void
onError(err: unknown) => void

Short-hand for instance.on(...) — useful for one-off integrations that don't want to keep a handle.


Resolved options (internal)#

After resolveOptions(raw), every field is either a default or a user-supplied value. Internal modules always depend on the ResolvedOptions shape. Key differences from the input:

  • themeMode (ThemePreference) and themeOverrides (ThemeOverrides) are split from the single theme input.
  • launcher.callout is either a Required<...> object or null (not undefined).
  • poweredBy is either PoweredByOption or null.
  • strings is fully populated Record<StringKey, string> (no missing keys).
  • stringsOverride is preserved separately so App can re-resolve into a new locale at runtime.
  • locale is a concrete BCP-47 tag ("auto" already resolved).

Full shape in src/core/config/types.ts ResolvedOptions.


Server settings#

ServerConfig (the handshake config field) is a subset of ChatWidgetOptions. Server can push anything that ships under the schemas above; callbacks (onMessage etc.) and identity fields (publicKey) are deliberately excluded.

Merge policy (src/core/config/merge.ts mergeServerConfig):

  • Scalars — user wins per-field. If the user didn't set it, server's value applies.
  • Objects — leaf merge. Server provides defaults; any user-set leaf wins.
  • header.actions — replace-whole list; server and user arrays are never concatenated.
  • i18n.strings — locale-aware merge; flat-form overrides apply to the active locale only, nested-form overrides apply per-locale.

Runtime safety#

The IIFE auto-boot bundle does NOT run Zod at runtime — pulling Zod in would push the bundle ~70 KB gz over budget. Instead, every field flows through resolveOptions in src/core/config/resolve.ts, which uses ?? default fallbacks for every option. A bad value (wrong enum, malformed length, etc.) flows through and the downstream consumer either uses the value as-is or substitutes a sane default — the widget always mounts.

For strict validation (the dashboard / dev configurator), import schemas directly from the @helpai/elements/schema package subpath and call .safeParse(). That path DOES include Zod; it's only loaded in admin contexts, not on every customer page.

Pass footer: { poweredBy: { text: "" } } to opt out of the brand attribution.

Still need help?

Ask the community