Live Chat
Audience: Sales · Support · QA · Management · Engineering · Where in app: Chats (agent dashboard) · configured under Settings → AI → Agents and Settings → Notifications · Plan availability: All plans
Live chat is Knock Knock's bread-and-butter channel: text messaging between a website visitor and either an AI agent, a human agent, or both in sequence. A visitor opens the chat in the widget, optionally answers a couple of contact-capture questions, and then talks to whoever the company's routing configuration sends them to. Everything is real-time over socket.io, agents work it from the Chats screen, and when the chat ends the transcript can be emailed out and mined for facts (sentiment, outcome, topics, contact info).
What it does
- Lets a visitor message the company from the widget and get an answer from AI, a human, or AI-then-human.
- Routes and queues chats to available agents, respecting working hours and per-agent capacity.
- Captures contact info before and during the chat and feeds it into the identity graph.
- Streams AI responses token-by-token so replies feel live.
- Lets agents transfer/take over a chat, with a strict ownership lock so two agents never collide.
- Mirrors chats to Firestore so the mobile/desktop apps stay in sync.
- Emails transcripts and extracts structured facts when the chat ends.
- Bridges to Slack so agents can answer a visitor straight from a Slack thread.
How it works
Lifecycle
- Visitor clicks chat in the widget.
- If contact-information questions are enabled (
contact_information_questions.*.enabled), they are asked first. Captured name/email/phone are passed with the first message ascontact_info. - The widget joins the ms-communication socket and emits
join roomwith theuser_session_id; a chat record (ChatModel) is created (or reused) keyed onuser_session_id. - Routing is decided from
companies.ai_config.chat_routing(see table below). - Messages flow over socket.io via the
messageevent. AI responses stream back asstream-message-chunkdeltas followed by astream-message-final. - Agents see live chats in Chats and can reply, transfer, or take over.
- On end, the live chat (
ChatModelin ms-communication) is consolidated into the backendchatscollection; if fact extraction is enabled, it is queued to ms-ai.
Where chats live
ChatModelin ms-communication (chats) — the live record:messages[],agent_id,company_id,user_session_id,created_by(agent/chatbot/user),ended_at,last_message_at,transcript_email_sent,previous_chats[](archived rounds),auto_triggers_count,channel.chatsin backend (consolidated) — addscontact_information,transfer_to/transferred_by,transcript_email_sent,fact_extraction_state, and aknox.*enrichment block (visitor_id,duration,turn_count,sentiment,outcome,topics_covered,questions_asked_by_visitor,agent_failed_to_answer_count).chat_queue(ChatQueueModel) — pending chats waiting for an agent, FIFO bycreated_at.- Firestore — chats are mirrored (
firestore.upsertChatMessage) so mobile/desktop apps get live updates.
Real-time events (socket.io, ms-communication)
The widget connects to three socket namespaces (backend :8080, ms-communication :3009, ms-sessions :3003), websocket transport only, with infinite reconnection (1s → 5s backoff). Chat runs on the ms-communication namespace.
| Event | Direction | Purpose |
|---|---|---|
join room | visitor → server | Subscribe to the session room |
message | both | Send a chat message (payload includes correlation_id, agent_connected, user_tz, contact_info, calendar_booking_meta) |
stream-message-chunk | server → visitor | AI streaming delta ({ type, content }) |
stream-message-final | server → visitor | AI final answer |
typing | both | Typing indicator (10s re-emit window) |
message-seen | both | Read receipt (emitted to session + company rooms) |
message-rejected | server → agent | Ownership lock blocked this agent's message |
connect-to-agent | visitor → server / server → visitor | Request a human; or notify that a human joined |
chat-queue-claim | agent → server | Agent picks up a waiting chat (FIFO or a specific chat_id) |
chat-queue-position | server → visitor | Visitor's 1-based position in the queue |
chat-queue-update | server → agents | Queue changed; refresh agent UI |
leave-chat-queue | visitor → server | Visitor abandons the queue |
reset-chat | visitor → server | "New chat" — archives current round to previous_chats[], clears agent_id, unmutes AI |
ai-next-node | both | Advance a flow-based chatbot node |
chat-summary | server → agents | Auto-trigger 5-message summary (headline, urgency, issue, contact) |
new-session-message / new-notification | server → company | Inbox/bell updates |
chatbot-auto-trigger | server → visitor | Auto-trigger fired (increments auto_triggers_count) |
Configuration & options
Routing modes (companies.ai_config.chat_routing)
| Mode | What happens |
|---|---|
ai | The AI Chat Agent answers. No human replies unless an admin manually takes over. agent_status is forced to no_human_agents. |
human (default) | Goes to a human. If chat_queue is enabled, the chat is queued for the next available agent; AI does not answer. |
ai_to_human | The AI answers first; when the AI signals connect_to_agent (or a takeover condition is met), the chat is escalated to the human queue. |
Queue settings (ChatQueueHelper)
| Setting | Effect |
|---|---|
companies.chat_queue | Master switch for queueing on the human / ai_to_human path |
companies.chat_queue_max_per_agent | Max concurrent live chats per agent. 0 = unlimited. An agent at capacity cannot claim more |
| Sort order | FIFO — { created_at: 1 } |
Contact capture
- Pre-chat:
contact_information_questions.*.enabledgates the questions shown before the first message. Answers ride along ascontact_infoand are merged into the AI's context so it does not re-ask. - Mid-chat: when the AI detects shared contact details, ms-communication calls ms-ai's contact extractor (
/agents/extract-contact-details), then posts the result to the backend identity graph (/identity-graph/resolve,source: 'chat') for dedup/merge onto the session.
Transcript email (companies.notifications.chat_transcript)
When enabled, after a chat ends the transcript is emailed to chat_transcript_emails_to[] (falls back to the company owner). The email includes the full message log, extracted contact info, and duration. The transcript_email_sent / transcript_email_sent_at fields on the chat prevent duplicate sends. (Note: the code also reads a send_chat_transcript flag, which defaults to true when unset — verify which flag your tenant uses in the UI.)
Slack (bidirectional)
If Slack is connected, new-visitor / chat events post to the configured channel, and an agent can reply to the visitor directly from the Slack thread — SlackService.handleSlackMessageForVisitor saves the reply (subject to the ownership lock) and emits it to the visitor as a normal message. Slack markdown, emoji, and file attachments are converted on the way through.
Behaviors & edge cases
- Agent ownership lock (the anti-collision guarantee). The first agent (or AI, or Slack replier) to send claims the chat via an atomic
findOneAndUpdateonagent_id. After that, a different non-privileged agent's message is rejected withmessage-rejected(reason: 'chat_locked', plusassigned_agent_id/assigned_agent_name).web_owner/adminusers bypass the lock. This is what stops a Slack reply or a second dashboard tab from silently hijacking a chat. QA: to reproduce a rejection, have two non-admin agents reply to the same chat. - First-message notification only. A
new-messagenotification fires only on the visitor's first message in a chat, so agents are not pinged on every line. - Duplicate-message guard. Messages are pushed atomically only if the
_idis not already present, preventing race-condition duplicates. - Reset/new chat.
reset-chatarchives the current round intoprevious_chats[], clearsmessages[]andagent_id, and unmutes the AI — the visitor effectively starts fresh while history is retained. - Working hours / offline. When the widget is online, chat opens per routing. Offline behavior:
- Offline +
human: the visitor goes through the offline chatbot questions flow and the transcript is emailed to the team (no live agent). - Offline +
ai: the AI takes over for 24/7 coverage. - Empty working-hours config = always open (24/7). Per-agent working-hours overrides exist (
agent.meta.working_hours,reply_outside_hours).
- Offline +
- Channel chats (WhatsApp/Instagram/SMS/Messenger). The same chat engine serves messaging channels. WhatsApp adds a deterministic rule engine (pre-AI signals: text, working hours, weekday, new/returning contact, message count; post-AI signals: intent, sentiment, confidence, KB match, spam) with actions
silent/canned/handoff/respond. A business reply sent from the WhatsApp app ("coexistence echo") flips the session tochatting_with: 'agent'so the AI stays quiet while a human handles the thread. - Rate limit. Chat messages are rate-limited at 100 points / 60s per connection (call requests are far stricter at 3 / 60s).
- AI defaults. When a company has not picked a model, chat falls back to a small default model/provider (e.g.
gpt-4o-mini/openaiin the message path) — verify current defaults, they change. - Correlation tracing. Every message round is traced through stages (
msg_received→msg_persisted→msg_sent_to_ai→msg_ai_first_chunk→msg_ai_final→msg_completed, plusmsg_locked/msg_dropped/msg_error) keyed bycorrelation_id— useful for engineers debugging "the AI didn't reply" reports.
Plan & limits
- Live chat (AI, human, and AI-to-human routing) is available on all plans.
- Per-agent chat capacity is configurable, not a plan limit (
chat_queue_max_per_agent). - Slack bidirectional chat and scheduled transcript handling may depend on the integration being connected and possibly the tier — verify against the current plan matrix.
- AI replies consume AI credits; running out can stop the AI from answering (see Notifications for the credits-low alert).
Technical implementation
- Owning service: ms-communication.
app/controllers/MessageController.js— the chat engine: message handling, routing, queue claim, streaming, summaries, contact extraction.app/services/MessagingService.js—saveChatMessage(ownership lock, Firestore mirror, queue removal, first-message notification).app/helpers/ChatQueueHelper.js— FIFO queue, per-agent capacity, position broadcasts.app/services/SocketService.js— socket rooms and rate limits.app/services/SlackService.js— bidirectional Slack chat (handleSlackMessageForVisitor).app/services/FirestoreService.js— chat mirroring for mobile/desktop.
- Widget: widget —
src/services/socket.ts(three namespaces),src/composables/useChatLogic.ts(send/stream/typing/seen handlers). - Agent UI: frontend —
src/views/ChatsView.vue(reply, transfer/accept-transfer, mark-seen, one-click call). - AI: ms-ai — Chat Agent, contact extraction, chat-summary notifications.
- Subsystems: Identity Graph (contact resolution), Knowledge Base / RAG (answers), Nox Assistant (transcript facts).
Related
- Calls — escalate a chat to a voice/video call
- Auto-Triggers — proactive chat pop-ups that start a chat
- Notifications — new-message, transfer, and transcript-email alerts
- ms-communication service
- Identity Graph