SDK & Widget · Mobile

Real-time (Socket.IO)

Receive replies, typing indicators and conversation events the moment they happen, and send messages over the same connection.

Inboxi uses Socket.IO v4 on the same host as the REST API (socketUrl from init). The socket is how a visitor receives replies live; sending can go either over the socket or over REST.

Connect
io(socketUrl)
widget_join
organizationId + visitor
widget_chat_history
Previous messages
widget_message
Live in both directions

Connect and join

After connecting, emit widget_join. Use the visitorId returned by init as userDetails.id, so the socket and the REST session refer to the same visitor. A join without a name is ignored.

javascript
import { io } from 'socket.io-client';

const socket = io(session.socketUrl, {
  query: { appId: String(session.organizationId) },
  transports: ['websocket', 'polling'],
  reconnection: true,
  reconnectionAttempts: 10,
  reconnectionDelay: 1000,
});

socket.on('connect', () => {
  socket.emit('widget_join', {
    organizationId: session.organizationId,
    context: { source: 'mobile_sdk' },   // add widgetId here if you run several widgets
    userDetails: {
      id: session.visitorId,
      name: user.name,
      email: user.email ?? null,
      phone: user.phone ?? null,
    },
  });
});

socket.on('widget_chat_history', ({ chatId, messages }) => renderHistory(chatId, messages));
socket.on('widget_message', (evt) => {
  if (evt.type === 'agent_message') renderReply(evt.message);
  if (evt.type === 'system_message') renderNotice(evt.message.content);
});
socket.on('widget_system_event', ({ type, data }) => handleEvent(type, data));
socket.on('widget_typing', ({ isTyping }) => showTyping(isTyping));
socket.on('widget_error', ({ code, message }) => console.warn(code, message));

// Send
socket.emit('widget_message', {
  organizationId: session.organizationId,
  visitorId: session.visitorId,
  text: 'Hello!',
});

Client → server events

EventPayloadDescription
widget_join{ organizationId, userDetails: { id, name, email?, phone? }, context?: { widgetId?, source? } }Joins the visitor's conversation. Required before sending. Re-emit after every reconnect.
widget_message{ organizationId, visitorId, text, mediaUrl?, mediaType? }Sends a visitor message. Triggers the AI agent if enabled.
widget_set_email{ visitorId, email }Saves an email on the conversation so your team can follow up.
widget_rating{ visitorId, rating }Rates the conversation, an integer from 1 to 5.

Server → client events

EventPayloadWhen
widget_chat_history{ chatId, messages: [{ id, content, sender, senderName, mediaUrl, mediaType, timestamp }] }Right after a join, when the conversation already has messages.
widget_message{ type: 'agent_message', message: { id, content, senderName, mediaUrl, mediaType, timestamp } }A reply from your team or the AI agent.
widget_message{ type: 'system_message', message: { id, content } }An automatic notice, such as a queue message.
widget_system_event{ type, data, timestamp }Conversation state changes: agent_joined (data.agentName), chat_resolved, chat_reopened, email_captured.
widget_typing{ isTyping }A teammate or the AI is typing.
widget_error{ code, message }Something went wrong: JOIN_FAILED, SESSION_NOT_FOUND, INVALID_EMAIL. See Errors.
An agent_message may carry a card object instead of text (for example product or choice cards in store widgets). Clients that do not render cards can skip messages with empty content.

Reconnection and reliability

  • Enable Socket.IO's built-in reconnection and emit widget_join again in every connect handler. Replies are delivered to the conversation room, which a socket enters only by joining.
  • If a widget_message arrives before the join is processed, Inboxi waits briefly; if no session appears you get SESSION_NOT_FOUND. Re-join and resend.
  • De-duplicate by message id: history after a reconnect can include messages you already rendered.
  • When the socket is down (app in the background, flaky network), send with POST /sdk/messages and rely on push for replies. Re-sync with the history endpoint when the app returns to the foreground.
  • Disconnect when the chat screen closes. A visitor with no open socket is treated as offline, which is what triggers push notifications.