Skip to main content
The chat components let your customers converse with one of your Digital Workers from inside your app — with streaming replies, inline forms, conversation history, unread badges, and live replies from a human CSM. All components on this page must be rendered inside a <StatisfyProvider>.

DigitalWorkerChat

The full chat widget. Outside a Statisfy portal, the only prop you need is the worker to talk to; inside a portal, see Config mode.

Props

Config mode (inside a Statisfy portal)

In a Statisfy-hosted Portal, you don’t hardcode the worker — the portal’s portal.runtime.json holds it, so a non-developer can repoint the chat from Settings without a code change. Pass a configId instead of a workerId:
Config is keyed by (component type, configId), so two instances sharing a configId intentionally share one config slot.
The configId must be a literal string. The portal save gate extracts it into the component manifest and rejects a missing or non-literal configIdconfigId={someVariable} fails validation. When the config value is unset, the component renders its not-configured state instead of chatting.

Embedding in a launcher

For a collapsible chat bubble, pass your open state to presented and badge the launcher with onUnreadChange:
Unread is tracked client-side (per-thread last-seen counts in localStorage, refreshed by a background poll roughly every 30s). The active thread auto-marks read while presented is true.

Live CSM replies

When a human CSM replies to a conversation from Statisfy, the chat detects the new message on its next poll and surfaces it in the open thread — and as unread elsewhere. No extra wiring is needed; just keep history enabled (the default).

OnboardingModule

A thin wrapper around DigitalWorkerChat for onboarding surfaces. It:
  • Reads the customer_id from the session token and pins initialThreadId to onboarding:<customer_id> — so the same customer always resumes the same onboarding thread.
  • Enables autoLoadConversation, so the chat opens with the right form (or closing message) already on screen and prior history restored across refreshes.
  • Defaults autoKickoff to true with an onboarding-specific kickoffPrompt, so the agent greets and presents the first step without the customer typing.
  • Sets scope to 'onboarding'.
  • Forces history and newChat off: onboarding is a single pinned thread per account, so there’s no thread switcher, no “New chat”, and no per-user unread badge.
  • Defaults formRenderer to DefaultFormRenderer.
OnboardingModuleProps is DigitalWorkerChatProps minus initialThreadId, autoLoadConversation, history, newChat, and scope — all five are derived or forced by the module, so accepting them would create two sources of truth. Every other chat prop (configId, workerName, greeting, className, formRenderer, autoKickoff, kickoffPrompt, presented, onUnreadChange, onClose, userKey) passes straight through.

Inline forms

When the agent needs structured input it emits a present_form tool call. Provide a formRenderer to render it as a real form; on submit, the chat posts a new turn with the collected values as structured_input. The SDK ships a ready-made renderer, DefaultFormRenderer:
To render forms in your own design system, supply a custom FormRenderer:
FormSpec has { prompt, fields }; each FormField has { key, label, type, options?, placeholder?, mandatory? }. The ctx.onSubmit(values) call replays the turn with those values; ctx.disabled is true while a submission is in flight.
If you omit formRenderer, present_form calls render as a generic “tool running” chip and the agent falls back to free-text input.

Headless client

Need full control of the conversation UI? Skip the components and drive the transport directly with createDigitalWorkerClient. (The provider isn’t required for the headless client — you pass config explicitly.)
The client exposes four methods:

sendMessage(content, threadId?, structuredInput?, formPrompt?)

One-shot send; resolves with the full reply.

streamMessage(options)

Streams the reply token-by-token over Server-Sent Events. Drive your UI from the handler callbacks.

loadConversation({ threadId, signal? })

Load a thread’s prior messages plus the orchestrator’s current next action.
Each message includes role (user | assistant | system), content, and a sender_kind (customer | agent | teammate) so you can distinguish AI replies from a human CSM’s.

listConversations({ source?, limit?, signal? })

List the customer’s conversations for a history panel.
Statisfy doesn’t store read-state — compute unread client-side by diffing each thread’s message_count against the last count you saw. See the API Reference for the underlying endpoints, request/response shapes, and error codes.