Skip to content

Events ​

Your host page can subscribe to events inside the widget — for example to show your own notification when a message arrives, or to update your own interface when an agent comes online.

Subscribing ​

There are two ways; both attach to the same event bus.

1. In the setup object (Configuration):

js
initWebfon({
  providerId: 'PROVIDER_ID',
  onMessage: (m) => console.log('new message', m),
  onAgentAvailable: (a) => console.log('agent online', a),
});

2. At runtime with onEvent — returns a function that unsubscribes:

js
// CDN
const off = window.Webfon.onEvent('message', (m) => console.log('new message', m));
off();   // unsubscribe

// npm
import { onEvent, offEvent } from '@webfon/client';
const off = onEvent('agentAvailable', (a) => console.log(a));

If your handler throws, the error is caught — neither the widget flow nor the other handlers are affected. An unknown event name is silently ignored: onEvent returns a no-op unsubscribe function.

Event list ​

EventSetup keyPayloadWhen
messageonMessagemessage object (below)A chat message arrives from the socket.
agentAvailableonAgentAvailableagent objectAn agent transitions to an available state (online/away).

message payload ​

js
{
  id, content,              // uuid · text (may be '': an attachment-only message)
  from_me,                  // bool — always false in this event (see below)
  send_at,                  // ISO date
  sender_type,              // 'agent' | 'system' | 'ai_agent'
  agent_id,                 // sending agent (not the assigned one) — null if absent
  file, file_type,          // bool · 'photo'|'video'|'audio'|'file'|null (first attachment)
  url, file_name, file_size,// shortcuts for the first attachment — null if absent
  media,                    // all attachments: [{id, url, mime, size, file_name}] ([] if absent)
  reply_to,                 // id of the replied-to message — null if absent
  is_pinned,                // bool
  reactions,                // { "<uid>": [{type:'emoji', emoji}] } — null if absent
}

Attachment URLs are temporary

url and media[].url are presigned addresses in private storage and are valid for about one hour. Do not store them permanently.

agentAvailable payload ​

js
{ user_id, first_name, last_name, avatar, title, status }   // status: 'online' | 'away'

It fires only on the transition — not for the full agent roster received at connection time. It also requires the agent roster to have loaded (the roster is fetched when the chat/call page first opens); state changes that arrive before the roster cannot be matched and are not emitted.

What does not trigger message ​

  • Messages you send — the widget drops your own messages (from_me) before emitting, so message always comes from the other side. Your own confirmation is a separate message_sent frame and emits no event.
  • History — the message history loaded when chat opens or reconnects emits no event; only live incoming messages do. A message that arrives twice still emits a single event.
  • Edit / reaction / pin updates.

Events only flow while the widget is connected ​

Not suitable for server-side notifications

The widget connects lazily — not the moment the page loads. It connects:

  • when the panel opens,
  • when the chat/call page first opens,
  • while another tab has a live conversation (then it connects in the background even while the widget is closed, so messages and notifications keep flowing).

If a visitor never opened the widget and has no open conversation, nothing is connected and message never fires. If you need to catch every message without missing any, use a server-side integration.

Subscription lifetime ​

Subscriptions are not tied to the widget instance: they stay registered after destroy(), so if the widget is destroyed and initialized again, the old handlers keep running. The same function reference is not registered twice, but if you pass a new inline function on every init (onMessage: (m) => …) the handlers accumulate.

In applications with a mount/unmount cycle, instead of passing the callback in the setup object, call the unsubscribe function returned by onEvent during cleanup:

jsx
useEffect(() => {
  const widget = initWebfon({ providerId: 'PROVIDER_ID' });
  const off = onEvent('message', (m) => console.log(m));
  return () => { off(); widget?.destroy(); };
}, []);

Webfon live-support widget documentation