Realtime events over WebSocket
Which WebSocket channels and events are available when connecting to AISAR Reverb directly, and how they relate to webhooks.
In addition to webhooks (see the Webhooks guide), AISAR broadcasts a subset of events in real time over a Pusher-compatible WebSocket server (Reverb). This is useful for custom frontends that need an instant reaction to changes (typing indicators, counters, conversation updates) without polling the REST API.
Channels
| Channel | Format | Purpose |
|---|---|---|
| Company | private-company.{companyId} | Conversation/message/contact/deal events for the whole team |
| User | private-user.{userId} | Personal events: counters, notifications |
| Channel | private-channel.{channelId} | Channel status, contact/group sync |
| Conversation | private-conversation.{conversationId} | Typing indicators and contact presence |
Active events
Messages (channel: company)
Broadcast: message.created, message.updated, message.deleted, message.read, message.status.updated, message.reaction.updated. Event payload:
{
"conversation_id": 750,
"thread_id": 750,
"message": { "...MessageResource..." }
}Conversations (channel: company)
Broadcast: conversation.created, conversation.updated, conversation.closed, conversation.reopened, conversation.assigned, conversation.unassigned.
{ "conversation_id": 750 }{
"conversation_id": 750,
"action": "added",
"participant": { "id": 1, "member_type": "user", "member_id": 5, "role": "agent" }
}Counters (channel: user)
chat.counters.updated refreshes the conversation filter badges. Fires on any counter-affecting event (message.created/deleted/read, conversation.created/updated/closed/reopened/assigned/unassigned) — recomputed personally for every user in the company.
{
"counters": {
"all": 42,
"awaiting": 5,
"unread": 8,
"assigned": 15,
"unassigned": 2,
"archived": 18
}
}Notifications (channel: user)
notification.created — a new personal notification for the user.
Messaging channels (channel: channel)
channel.realtime.update — a single event routed by payload.type: channel_status (channel status change), contacts_synced/contacts_updated, groups_synced/group_updated/group_participants_updated, history_sync_progress.
Deals and AI agents (channel: company)
deal.updated — a deal changed. ai-pending-action.updated — the status of an AI agent action awaiting operator confirmation changed (action: "created" | "approved" | "rejected"), payload { event_id, conversation_id, action }.
Broadcast-only events (not delivered as a webhook)
Some events are broadcast over WebSocket only, without a corresponding outbound HTTP webhook: contact.outbound_eligibility_updated (a contact's eligibility for outbound/WhatsApp calls, payload {contact_id, channel_id} or a variant with contact_account_id + call{...}) and nudges.updated (an in-app nudge banner, payload {company_id}). If your integration needs these specific signals, subscribe to them over WebSocket — no webhook fires for them.
Reliability
- Broadcast events raised inside a database transaction are only sent after it commits — you will never see an event for an entity that was then rolled back.
- Delivery is synchronous (not queued) — latency is minimal, but events aren't buffered for the duration of a disconnect.
- The
unreadcounter calculation depends on the company'sshared_read_receiptssetting: whentrueit uses the maximumlast_read_atacross all users, whenfalseeach user's individuallast_read_at.
AISAR connects to Reverb over a Pusher-compatible protocol (wsHost: api.aisar.app, TLS port 443, authEndpoint: https://api.aisar.app/broadcasting/auth). Subscribing to private channels (private-*) requires token authorization at /broadcasting/auth and goes through a per-channel ACL — a user from one company cannot subscribe to another company's channel. Contact AISAR support for the connection credentials (app key, etc.).