Skip to main content

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.

This is the 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:
Open the printed loopback customer URL. Select its demo conversation, press t to take over, then i to write a reply. Send a visitor message in the browser and confirm it reaches the console. The playground uses memory and a synthetic assistant; it does not call a provider, customer database or helpdesk. npm run tui -- --demo --once prints a non-interactive snapshot.

Connect to your API

Remote connections require HTTPS; loopback HTTP is accepted for development. The CLI does not load .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

  1. 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.
  2. 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.
  3. 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.
  4. Press a to return control to AI, or x to resolve and release the conversation. A new visitor question after resolution can resume AI.
Claims last two minutes and renew every 30 seconds while the console is connected. A lost connection can let the claim expire; the next visitor turn can resume AI and another operator can claim. Customer polls run while the widget is open; the terminal refreshes every three seconds. This transport is polling. A 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 use Authorization: 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.
Last modified on November 13, 2026