# How Baileys Manages Presence Updates: Typing Indicators and Online Status Explained

> Discover how Baileys manages WhatsApp presence updates like typing indicators and online status. Learn about raw node capture, parsing, and strongly-typed events.

- Repository: [WhiskeySockets/Baileys](https://github.com/WhiskeySockets/Baileys)
- Tags: internals
- Published: 2026-08-01

---

**Baileys handles WhatsApp presence updates through a dedicated `presence` WebSocket stanza that flows through raw node capture, parsing, and event emission, exposing strongly-typed `presence.update` events for developers to consume.**

Baileys, the popular open-source WhatsApp Web API from **WhiskeySockets/Baileys**, provides robust support for tracking contact presence—including typing indicators, online status, and offline states. Understanding how this system works is essential for building responsive chat applications that react to user activity in real time.

## The Three-Stage Presence Flow

Baileys processes presence information through a clean separation of concerns: raw protocol handling, data normalization, and public event emission.

### Stage 1: Raw WebSocket Capture

Incoming presence data arrives as `presence` stanzas over the WebSocket connection. The core socket module registers a dedicated listener to intercept these nodes before any processing occurs.

In [`src/Socket/socket.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Socket/socket.ts) around **line 1339**, the WebSocket event handler attaches a callback specifically for presence nodes:

```typescript
// From src/Socket/socket.ts
ws.on('CB:presence', handlePresenceUpdate)

```

This registration ensures all presence-related traffic from WhatsApp's servers is immediately captured and routed for parsing.

### Stage 2: Parsing and Data Normalization

The heavy lifting happens in [`src/Socket/chats.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Socket/chats.ts) between **lines 855–906**. Here, Baileys extracts the presence type and constructs a structured data object.

The parser handles multiple presence states:
- **`available`** — user is online
- **`unavailable`** — user is offline
- **`composing`** — user is typing
- **`paused`** — user stopped typing

If a malformed node arrives, the implementation logs the error and silently discards it rather than crashing the connection. This defensive approach maintains socket stability even with protocol edge cases.

The handler builds a `PresenceData` object containing `lastKnownPresence` and related metadata, then prepares it for public consumption.

### Stage 3: Event Emission

After successful parsing, Baileys emits the `presence.update` event via the internal event emitter at **line 906** in [`src/Socket/chats.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Socket/chats.ts):

```typescript
// From src/Socket/chats.ts
ev.emit('presence.update', { id, presences })

```

This emission transforms internal protocol data into a developer-friendly, strongly-typed event.

## Event Type Definition

The public API contract is defined in [`src/Types/Events.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Types/Events.ts) at **lines 57–58**:

```typescript
// From src/Types/Events.ts
'presence.update': { 
  id: string; 
  presences: { [participant: string]: PresenceData } 
}

```

The `PresenceData` type (defined in [`src/Types/Message.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Types/Message.ts)) includes:
- `lastKnownPresence`: the core presence state
- `isOnline`: boolean convenience flag
- Timestamp fields for tracking state changes

## Subscribing to Presence Updates

Developers interact with presence functionality through two primary patterns: reactive event listeners and proactive subscription requests.

### Listening for Presence Changes

```typescript
import makeWASocket, { useSingleFileAuthState } from '@adiwajshing/baileys'

const { state } = useSingleFileAuthState('./auth_info.json')
const sock = makeWASocket({
  auth: state,
  printQRInTerminal: true,
})

// React to typing and online status changes
sock.ev.on('presence.update', ({ id, presences }) => {
  for (const [participant, presence] of Object.entries(presences)) {
    switch (presence.lastKnownPresence) {
      case 'composing':
        console.log(`${participant} is typing...`)
        break
      case 'available':
        console.log(`${participant} is online`)
        break
      case 'unavailable':
        console.log(`${participant} is offline`)
        break
      case 'paused':
        console.log(`${participant} stopped typing`)
        break
    }
  }
})

```

### Requesting Presence for Specific Contacts

By default, WhatsApp does not broadcast presence for all contacts. You must explicitly subscribe using `presenceSubscribe()`:

```typescript
// Request presence updates for a specific JID
await sock.presenceSubscribe('1234567890@s.whatsapp.net')

// Group subscriptions work similarly
await sock.presenceSubscribe('1234567890-1234567890@g.us')

```

According to the Baileys source code, `presenceSubscribe` is implemented in [`src/Socket/chats.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Socket/chats.ts) and sends a protocol-level subscription request to WhatsApp's servers.

## Design Rationale

The Baileys architecture for presence updates emphasizes three qualities:

- **Separation of concerns** — Protocol parsing remains isolated in [`chats.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/chats.ts) while the public API exposes clean events
- **Fault tolerance** — Malformed nodes trigger logged errors without disrupting the WebSocket connection
- **Extensibility** — New presence types require only parser updates, not structural changes

## Summary

- Baileys captures raw `presence` stanzas in [`src/Socket/socket.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Socket/socket.ts) via the `CB:presence` WebSocket event
- [`src/Socket/chats.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Socket/chats.ts) parses nodes between lines 855–906, creating `PresenceData` objects
- Clean `presence.update` events are emitted at line 906, with types declared in [`src/Types/Events.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Types/Events.ts)
- Developers use `sock.ev.on('presence.update', …)` to react to changes and `sock.presenceSubscribe(jid)` to request updates

## Frequently Asked Questions

### How do I know when someone is typing in Baileys?

Listen for the `presence.update` event and check `presence.lastKnownPresence === 'composing'`. This state indicates the contact is actively typing. The `'paused'` state fires when typing stops without sending a message.

### Why am I not receiving presence updates for some contacts?

WhatsApp requires explicit subscriptions for presence data. Call `await sock.presenceSubscribe(jid)` for each contact or group you want to monitor. Without this step, the WhatsApp server will not push presence information to your socket.

### What presence states does Baileys support?

According to the parser implementation in [`src/Socket/chats.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Socket/chats.ts), Baileys handles: `available` (online), `unavailable` (offline), `composing` (typing), and `paused` (stopped typing). These map directly to WhatsApp's protocol-level presence indicators.

### Where is the PresenceData type defined?

The `PresenceData` interface is located in [`src/Types/Message.ts`](https://github.com/WhiskeySockets/Baileys/blob/main/src/Types/Message.ts). It includes `lastKnownPresence` as a string literal union, boolean flags, and optional timestamp fields that provide complete status context for each contact.