<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’sportal.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:
(component type, configId), so two instances sharing a configId intentionally share one config slot.
Embedding in a launcher
For a collapsible chat bubble, pass your open state topresented 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 keephistory enabled (the default).
OnboardingModule
A thin wrapper around DigitalWorkerChat for onboarding surfaces. It:
- Reads the
customer_idfrom the session token and pinsinitialThreadIdtoonboarding:<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
autoKickofftotruewith an onboarding-specifickickoffPrompt, so the agent greets and presents the first step without the customer typing. - Sets
scopeto'onboarding'. - Forces
historyandnewChatoff: onboarding is a single pinned thread per account, so there’s no thread switcher, no “New chat”, and no per-user unread badge. - Defaults
formRenderertoDefaultFormRenderer.
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 apresent_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:
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 withcreateDigitalWorkerClient. (The provider isn’t required for the headless client — you pass config explicitly.)
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.
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.
message_count against the last count you saw.
See the API Reference for the underlying endpoints, request/response shapes, and error codes.