Events
Messaging — Events
Real-time updates come over Socket.IO, namespace /messaging/events.
Connect with the admin JWT in the handshake (auth.token, query.token, or the
Authorization header). The tenant is derived from the request host.
On connect you auto-join your visible rooms: tenant:{db}, admin:{id}, and each
session:{slug} you're allowed to see. The server emits connected on success, or
error (AUTH_MISSING / AUTH_INVALID / ADMIN_NOT_FOUND) then disconnects.
Server → client
The whole event object is the payload (socket.on(type, event)):
| Event | Fires when | Key fields |
|---|---|---|
message | A message is sent or received | payload = the message |
message-ack | Delivery/read status changes | { messageSlug, chatSlug, providerId, ack, acknowledgment } |
chat-read | A chat is marked read | chat + reader |
chat.answered | "Answered" toggled | chat |
session.status.updated | Session connects/disconnects | sessionSlug, status |
message-edited · message-revoked | Message edited/deleted (WAHA) | message |
call | Inbound/missed call | call |
presence.update | Contact online/typing | phone, status |
label.upsert · label.deleted · label.chats.add · label.chats.delete | Label changes | label / chat |
dedup.outcome | Duplicate contact resolved | record |
Fan-out is filtered: broadcastToSession only emits to admins that
ChatVisibilityPolicy.selectVisibleAdmins allows, so a client never receives a
chat it can't access.
Client → server
Emit these with { sessionSlug, chatSlug } (or { sessionSlug }):
| Event | Meaning |
|---|---|
online / offline | Presence for a session |
startTyping / stopTyping | Typing indicator |
startRecording / stopRecording | Voice recording indicator |
seen | Mark a chat seen |
Internal domain events
Messaging also listens to CRM events (server-side only, not over the socket):
| Event | Effect |
|---|---|
notification.admin-assigned.whatsapp.requested | WhatsApp-notify the assigned admin |
comment.mentioned | WhatsApp-notify the mentioned admin |