How OpenWA Emits WebSocket Real-Time Events for Session Status

OpenWA uses a room-based Socket.io gateway to broadcast session status changes to subscribed WebSocket clients through four distinct channel patterns, enabling both targeted and wildcard subscriptions.

OpenWA leverages Socket.io to deliver real-time updates about WhatsApp session states to connected clients. When a session's status changes in the database, the system immediately pushes WebSocket real-time events to any client subscribed to that specific session or monitoring all sessions via wildcard patterns.

The Session Status Event Flow

Triggering Updates from SessionService

Whenever a session's status column updates, the SessionService immediately notifies the events gateway. In src/modules/session/session.service.ts, the updateStatus method invokes this.eventsGateway.emitSessionStatus(id, status) to initiate the broadcast sequence (lines 82-90).

This tight integration ensures that database state changes propagate to connected clients without polling delays.

Gateway Emission Method

The EventsGateway class in src/modules/events/events.gateway.ts provides the emitSessionStatus method (lines 207-210) that constructs the payload and delegates to the room emitter:

emitSessionStatus(sessionId: string, status: string, data?: Record<string, unknown>) {
  this.emitToRooms(sessionId, 'session.status', { status, ...data });
}

This method accepts optional additional data to enrich the status payload beyond the basic state string.

Room-Based Broadcasting Strategy

The private emitToRooms method (lines 188-202) broadcasts a standardized WSEventMessage to four distinct rooms simultaneously, ensuring clients receive updates regardless of their subscription granularity:

  • session:{sessionId}:session.status – specific session and specific event
  • session:{sessionId}:* – all events for that specific session
  • session:*:{event} – specific event across all sessions
  • session:*:* – global catch-all for any session event
private emitToRooms(sessionId: string, event: string, data: unknown): void {
  const eventMessage: WSEventMessage = {
    type: 'event',
    payload: { event, sessionId, data },
    timestamp: new Date().toISOString(),
  };
  this.server.to(buildRoomName(sessionId, event)).emit('message', eventMessage);
  this.server.to(buildRoomName(sessionId, '*')).emit('message', eventMessage);
  this.server.to(buildRoomName('*', event)).emit('message', eventMessage);
  this.server.to(buildRoomName('*', '*')).emit('message', eventMessage);
}

The buildRoomName utility constructs these channel strings consistently across the gateway.

Client Subscription Architecture

Connection and Authentication

Clients connect to the /events namespace and authenticate using an API key passed as a query parameter. The gateway validates the connection before accepting subscription requests, ensuring only authorized clients receive WebSocket real-time events.

Subscription Message Protocol

To receive status updates, clients send a subscription message matching the schema defined in src/modules/events/dto/ws-messages.dto.ts (lines 13-20). The handleSubscribe method in the gateway (lines 98-130) processes these requests and joins the socket to the appropriate rooms based on the requested sessionId and events array.

{
  "type": "subscribe",
  "sessionId": "<session-id> | *",
  "events": ["session.status"],
  "requestId": "req-123"
}

Setting sessionId to * subscribes the client to status changes for all sessions, while specific IDs limit updates to individual sessions.

Client Implementation Example

The following TypeScript example demonstrates connecting to the OpenWA events gateway, subscribing to session status updates, and handling incoming messages:

import { io, Socket } from 'socket.io-client';

// Connect with API key authentication
const socket: Socket = io('https://your-openwa-host/events', {
  query: { apiKey: 'YOUR_API_KEY' },
});

// Subscribe to specific session status events upon connection
socket.on('connect', () => {
  const subscription = {
    type: 'subscribe',
    sessionId: 'my-session-id', // Use '*' for all sessions
    events: ['session.status'],
    requestId: 'req-123',
  };
  socket.emit('message', subscription);
});

// Listen for real-time status updates
socket.on('message', (payload) => {
  if (payload.type === 'event' && payload.payload.event === 'session.status') {
    console.log('Session status changed:', payload.payload.data);
    console.log('Timestamp:', payload.timestamp);
    console.log('Session ID:', payload.payload.sessionId);
  }
});

Summary

  • Room-based architecture: OpenWA broadcasts to four room patterns simultaneously (session:{id}:{event}, session:{id}:*, session:*:{event}, session:*:*), supporting both precise and wildcard subscriptions.
  • Service-to-gateway flow: SessionService.updateStatus triggers EventsGateway.emitSessionStatus, which delegates to emitToRooms for actual broadcasting.
  • Authentication: Clients authenticate via API key query parameters when connecting to the /events namespace.
  • Schema definition: Message structures are defined in src/modules/events/dto/ws-messages.dto.ts, while subscription logic resides in src/modules/events/events.gateway.ts.

Frequently Asked Questions

How does OpenWA authenticate WebSocket connections?

Clients pass an API key via query parameters when connecting to the /events namespace. The gateway validates this key before allowing the socket to join rooms or receive WebSocket real-time events.

What are the four room patterns used for broadcasting?

OpenWA broadcasts every event to four rooms: the specific session-plus-event room, the all-events-for-session room, the specific-event-for-all-sessions room, and a global catch-all room. This design allows clients to subscribe at whatever granularity their use case requires.

Can a client subscribe to multiple event types in one request?

Yes. The events array in the subscription message accepts multiple strings, allowing clients to receive session.status, session.qr, and other events simultaneously through a single subscription request handled by EventsGateway.handleSubscribe.

Where is the message schema defined for WebSocket communication?

The TypeScript interfaces and DTOs for subscription requests, event payloads, and message wrappers are defined in src/modules/events/dto/ws-messages.dto.ts (lines 13-20), ensuring type safety across the OpenWA event system.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →