# How OpenWA Emits WebSocket Real-Time Events for Session Status

> Discover how OpenWA emits WebSocket real-time events for session status using a room-based Socket.io gateway. Learn about targeted and wildcard channel subscriptions for seamless updates.

- Repository: [Yudhi Armyndharis/OpenWA](https://github.com/rmyndharis/OpenWA)
- Tags: internals
- Published: 2026-05-21

---

**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`](https://github.com/rmyndharis/OpenWA/blob/main/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`](https://github.com/rmyndharis/OpenWA/blob/main/src/modules/events/events.gateway.ts) provides the `emitSessionStatus` method (lines 207-210) that constructs the payload and delegates to the room emitter:

```typescript
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

```typescript
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`](https://github.com/rmyndharis/OpenWA/blob/main/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.

```json
{
  "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:

```typescript
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`](https://github.com/rmyndharis/OpenWA/blob/main/src/modules/events/dto/ws-messages.dto.ts), while subscription logic resides in [`src/modules/events/events.gateway.ts`](https://github.com/rmyndharis/OpenWA/blob/main/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`](https://github.com/rmyndharis/OpenWA/blob/main/src/modules/events/dto/ws-messages.dto.ts) (lines 13-20), ensuring type safety across the OpenWA event system.