Outcome: a signed visitor conversation receives a persisted operator reply through the standalone widget backend.
Prerequisites: a backend with native live chat enabled, shared Redis for multiple replicas, and a separate tenant-scoped operator key per person.
response-chat-widget server’s native terminal workflow. The hosted ResponseCX
browser desk has a separate setup. Native takeover also
does not synchronize replies into an external helpdesk ticket.
Try the local playground
From the widget repository, with Node 20.20.0 and built browser assets:npm run tui -- --demo --once prints a non-interactive snapshot.
Connect to your API
.env automatically. Keep the key in the operator environment, outside storefront
config and source control. See site administration
for schema prerequisites and first-key bootstrap.
On the backend enable WIDGET_LIVE_CHAT_ENABLED=true. Visitors need valid signed widget
tokens so new threads have verified ownership. Ownerless legacy threads are excluded from
the inbox; caller-supplied tenant metadata cannot adopt them. Set threadPollIntervalMs: 1500
on the visitor widget, or use the session-enabled embed which supplies that default.
Claim, reply and return
- Select a conversation and press t to take over. Only the operator key that owns the claim can send new replies, renew, release or resolve it.
- Send a visitor message. While takeover is active, the server persists it and returns
{ liveChat: { accepted: true, status: "active", operatorName }, reply: "" }, including when streaming was requested. The widget adds no artificial assistant reply. - Press i, write a human reply and press Enter to send. The server acknowledges it only after thread persistence. Confirm the visitor receives it on a subsequent thread poll.
- Press a to return control to AI, or x to resolve and release the conversation. A new visitor question after resolution can resume AI.
thread_busy response means a shared customer/AI/operator
write is finishing; wait and refresh before trying again.
Verify: one visitor message is stored once, the owning operator’s reply appears once, and AI stays paused until release or claim expiry.
Inbox and private working state
Drafts, local unread markers and searches last for the console session. Saved replies insert
text for review; insertion does not send. The inbox scans at most the latest 500 active threads
before tenant filtering. It is a bounded work queue, not a historical search archive.
Private notes have separate routes and never enter customer transcripts or model context.
Any operator in the owning organization can save a note without taking over. Each note uses
clientNoteId, is limited to 4000 characters, and the thread holds at most 100 notes/64,000
characters. The terminal displays incoming attachment labels and sends text replies.
API clients and uncertain delivery
Native operator routes useAuthorization: Bearer rw_admin_..., tenant ownership checks, Cache-Control: no-store, and
120 requests/minute per credential. Mutations share the same distributed thread lease as AI
and customer writes.
For a reply, supply { text, clientMessageId } with a stable 8–128 character word/hyphen ID;
text is limited to 8000 characters. Repeating the stored ID with the same trimmed text and
operator replays the persisted result, even if the claim has since been released. Changed
content or operator returns message_id_conflict. Notes use the corresponding
clientNoteId/note_id_conflict contract. Preserve the receipt ID while reconciling a timeout;
using a new ID can duplicate an accepted message or note.
Before enabling this across customer traffic, run the staged checks in
deployment on every replica.