Skip to main content

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

  1. Visitor clicks chat in the widget.
  2. If contact-information questions are enabled (contact_information_questions.*.enabled), they are asked first. Captured name/email/phone are passed with the first message as contact_info.
  3. The widget joins the ms-communication socket and emits join room with the user_session_id; a chat record (ChatModel) is created (or reused) keyed on user_session_id.
  4. Routing is decided from companies.ai_config.chat_routing (see table below).
  5. Messages flow over socket.io via the message event. AI responses stream back as stream-message-chunk deltas followed by a stream-message-final.
  6. Agents see live chats in Chats and can reply, transfer, or take over.
  7. On end, the live chat (ChatModel in ms-communication) is consolidated into the backend chats collection; if fact extraction is enabled, it is queued to ms-ai.

Where chats live

  • ChatModel in 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.
  • chats in backend (consolidated) — adds contact_information, transfer_to/transferred_by, transcript_email_sent, fact_extraction_state, and a knox.* 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 by created_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.

EventDirectionPurpose
join roomvisitor → serverSubscribe to the session room
messagebothSend a chat message (payload includes correlation_id, agent_connected, user_tz, contact_info, calendar_booking_meta)
stream-message-chunkserver → visitorAI streaming delta ({ type, content })
stream-message-finalserver → visitorAI final answer
typingbothTyping indicator (10s re-emit window)
message-seenbothRead receipt (emitted to session + company rooms)
message-rejectedserver → agentOwnership lock blocked this agent's message
connect-to-agentvisitor → server / server → visitorRequest a human; or notify that a human joined
chat-queue-claimagent → serverAgent picks up a waiting chat (FIFO or a specific chat_id)
chat-queue-positionserver → visitorVisitor's 1-based position in the queue
chat-queue-updateserver → agentsQueue changed; refresh agent UI
leave-chat-queuevisitor → serverVisitor abandons the queue
reset-chatvisitor → server"New chat" — archives current round to previous_chats[], clears agent_id, unmutes AI
ai-next-nodebothAdvance a flow-based chatbot node
chat-summaryserver → agentsAuto-trigger 5-message summary (headline, urgency, issue, contact)
new-session-message / new-notificationserver → companyInbox/bell updates
chatbot-auto-triggerserver → visitorAuto-trigger fired (increments auto_triggers_count)

Configuration & options

Routing modes (companies.ai_config.chat_routing)

ModeWhat happens
aiThe 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_humanThe 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)

SettingEffect
companies.chat_queueMaster switch for queueing on the human / ai_to_human path
companies.chat_queue_max_per_agentMax concurrent live chats per agent. 0 = unlimited. An agent at capacity cannot claim more
Sort orderFIFO — { created_at: 1 }

Contact capture

  • Pre-chat: contact_information_questions.*.enabled gates the questions shown before the first message. Answers ride along as contact_info and 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 threadSlackService.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 findOneAndUpdate on agent_id. After that, a different non-privileged agent's message is rejected with message-rejected (reason: 'chat_locked', plus assigned_agent_id / assigned_agent_name). web_owner / admin users 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-message notification 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 _id is not already present, preventing race-condition duplicates.
  • Reset/new chat. reset-chat archives the current round into previous_chats[], clears messages[] and agent_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).
  • 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 to chatting_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 / openai in the message path) — verify current defaults, they change.
  • Correlation tracing. Every message round is traced through stages (msg_receivedmsg_persistedmsg_sent_to_aimsg_ai_first_chunkmsg_ai_finalmsg_completed, plus msg_locked / msg_dropped / msg_error) keyed by correlation_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.jssaveChatMessage (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: widgetsrc/services/socket.ts (three namespaces), src/composables/useChatLogic.ts (send/stream/typing/seen handlers).
  • Agent UI: frontendsrc/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).