> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/whiskeysockets/Baileys/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling Events

> Complete guide to listening and responding to WhatsApp events in Baileys with real-world examples

## Overview

Baileys uses an EventEmitter-based architecture for all WhatsApp interactions. Every action - from receiving messages to connection updates - is emitted as a typed event.

<Note>
  All events are fully typed with TypeScript, providing excellent IntelliSense support in modern editors like VS Code.
</Note>

## Event Architecture

Baileys provides two ways to handle events:

<Tabs>
  <Tab title="Individual Listeners">
    ```typescript theme={null}
    sock.ev.on('messages.upsert', ({ messages }) => {
        console.log('Received messages:', messages)
    })

    sock.ev.on('connection.update', (update) => {
        console.log('Connection status:', update.connection)
    })
    ```
  </Tab>

  <Tab title="Batch Processing">
    ```typescript theme={null}
    sock.ev.process(async (events) => {
        // Process multiple event types efficiently
        if (events['messages.upsert']) {
            // Handle messages
        }
        
        if (events['connection.update']) {
            // Handle connection
        }
    })
    ```
  </Tab>
</Tabs>

<Warning>
  The batch processing method (`ev.process`) is more efficient as it processes events in batches, reducing overhead.
</Warning>

## Core Events

### Connection Events

#### connection.update

Emitted when connection state changes (connecting, open, close).

```typescript theme={null}
import { Boom } from '@hapi/boom'
import { DisconnectReason } from '@whiskeysockets/baileys'

sock.ev.on('connection.update', (update) => {
    const { connection, lastDisconnect, qr } = update
    
    if (qr) {
        console.log('QR Code:', qr)
    }
    
    if (connection === 'close') {
        const shouldReconnect = 
            (lastDisconnect?.error as Boom)?.output?.statusCode !== 
            DisconnectReason.loggedOut
        
        if (shouldReconnect) {
            startSock() // Reconnect
        } else {
            console.log('Logged out!')
        }
    } else if (connection === 'open') {
        console.log('Connection opened')
    }
})
```

#### creds.update

Emitted when authentication credentials are updated. **Critical** for session persistence.

```typescript theme={null}
import { useMultiFileAuthState } from '@whiskeysockets/baileys'

const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')

const sock = makeWASocket({ auth: state })

// Save credentials whenever they update
sock.ev.on('creds.update', saveCreds)
```

<Warning>
  Failing to save credentials when `creds.update` fires will cause message delivery issues and require re-authentication.
</Warning>

### Message Events

#### messages.upsert

Receive new messages or message history.

```typescript theme={null}
sock.ev.on('messages.upsert', ({ messages, type }) => {
    console.log('Message type:', type) // 'notify' | 'append' | 'prepend'
    
    for (const msg of messages) {
        if (!msg.message) continue // Ignore empty messages
        
        const text = msg.message.conversation || 
                     msg.message.extendedTextMessage?.text
        
        console.log('From:', msg.key.remoteJid)
        console.log('Text:', text)
        console.log('From me:', msg.key.fromMe)
    }
})
```

<Note>
  **Important:** Always use a loop like `for (const message of messages)` to handle all messages in the array. The event may contain multiple messages.
</Note>

##### Message Types

* `notify` - New message received while online
* `append` - Message added to the end of history
* `prepend` - Message added to the beginning of history

##### Auto-Reply Example

```typescript theme={null}
import { generateMessageIDV2, isJidNewsletter } from '@whiskeysockets/baileys'

sock.ev.on('messages.upsert', async ({ messages, type }) => {
    if (type !== 'notify') return // Only respond to new messages
    
    for (const msg of messages) {
        // Skip messages from self, no message content, or newsletters
        if (msg.key.fromMe || !msg.message || isJidNewsletter(msg.key.remoteJid)) {
            continue
        }
        
        const text = msg.message.conversation || 
                     msg.message.extendedTextMessage?.text
        
        if (text) {
            const messageId = generateMessageIDV2(sock.user?.id)
            await sock.sendMessage(
                msg.key.remoteJid!, 
                { text: `Echo: ${text}` },
                { messageId }
            )
        }
    }
})
```

#### messages.update

Emitted when message metadata changes (delivered, read, deleted, edited, poll votes).

```typescript theme={null}
sock.ev.on('messages.update', (updates) => {
    for (const { key, update } of updates) {
        console.log('Message ID:', key.id)
        console.log('Update:', update)
        
        // Check for poll updates
        if (update.pollUpdates) {
            console.log('Poll votes received')
        }
    }
})
```

##### Decrypt Poll Votes

```typescript theme={null}
import { getAggregateVotesInPollMessage } from '@whiskeysockets/baileys'

// You need a getMessage implementation (store)
const getMessage = async (key: WAMessageKey) => {
    // Retrieve message from your store
    return await getMessageFromStore(key)
}

sock.ev.on('messages.update', async (updates) => {
    for (const { key, update } of updates) {
        if (update.pollUpdates) {
            const pollCreation = await getMessage(key)
            
            if (pollCreation) {
                const aggregatedVotes = getAggregateVotesInPollMessage({
                    message: pollCreation,
                    pollUpdates: update.pollUpdates,
                })
                
                console.log('Poll results:', aggregatedVotes)
            }
        }
    }
})
```

#### messages.delete

Emitted when messages are deleted.

```typescript theme={null}
sock.ev.on('messages.delete', (deletion) => {
    if ('keys' in deletion) {
        // Specific messages deleted
        console.log('Deleted message keys:', deletion.keys)
    } else if (deletion.all) {
        // All messages in chat deleted
        console.log('All messages deleted from:', deletion.jid)
    }
})
```

#### messages.reaction

Emitted when a message receives a reaction.

```typescript theme={null}
sock.ev.on('messages.reaction', (reactions) => {
    for (const { key, reaction } of reactions) {
        console.log('Message:', key.id)
        console.log('Reaction:', reaction.text) // emoji or empty string if removed
        console.log('From:', reaction.key?.remoteJid)
    }
})
```

#### message-receipt.update

Emitted when message read/delivery status changes.

```typescript theme={null}
sock.ev.on('message-receipt.update', (receipts) => {
    for (const receipt of receipts) {
        console.log('Message:', receipt.key.id)
        console.log('Receipt:', receipt.receipt)
    }
})
```

### Chat Events

#### chats.upsert

New chats added.

```typescript theme={null}
sock.ev.on('chats.upsert', (chats) => {
    console.log('New chats:', chats.length)
    
    for (const chat of chats) {
        console.log('Chat ID:', chat.id)
        console.log('Name:', chat.name)
        console.log('Unread count:', chat.unreadCount)
    }
})
```

#### chats.update

Chat metadata updated (unread count, last message, etc.).

```typescript theme={null}
sock.ev.on('chats.update', (updates) => {
    for (const update of updates) {
        console.log('Updated chat:', update.id)
        
        if (typeof update.unreadCount !== 'undefined') {
            console.log('New unread count:', update.unreadCount)
        }
    }
})
```

#### chats.delete

Chats deleted.

```typescript theme={null}
sock.ev.on('chats.delete', (deletedChats) => {
    console.log('Deleted chat IDs:', deletedChats)
})
```

### Contact Events

#### contacts.upsert

New contacts added or discovered.

```typescript theme={null}
sock.ev.on('contacts.upsert', (contacts) => {
    for (const contact of contacts) {
        console.log('Contact:', contact.id)
        console.log('Name:', contact.name)
        console.log('Notify:', contact.notify)
    }
})
```

#### contacts.update

Contact information updated (profile picture, name, etc.).

```typescript theme={null}
sock.ev.on('contacts.update', async (updates) => {
    for (const contact of updates) {
        if (typeof contact.imgUrl !== 'undefined') {
            // Profile picture changed
            const newUrl = contact.imgUrl === null
                ? null
                : await sock.profilePictureUrl(contact.id!).catch(() => null)
            
            console.log(`${contact.id} has new profile pic:`, newUrl)
        }
    }
})
```

### Group Events

#### groups.upsert

New groups discovered.

```typescript theme={null}
sock.ev.on('groups.upsert', (groups) => {
    for (const group of groups) {
        console.log('Group:', group.id)
        console.log('Subject:', group.subject)
        console.log('Participants:', group.participants.length)
    }
})
```

#### groups.update

Group metadata changed (subject, description, settings).

```typescript theme={null}
sock.ev.on('groups.update', async (updates) => {
    for (const update of updates) {
        console.log('Group updated:', update.id)
        
        if (update.subject) {
            console.log('New subject:', update.subject)
        }
        
        if (update.desc) {
            console.log('New description:', update.desc)
        }
    }
})
```

#### group-participants.update

Group participants added, removed, promoted, or demoted.

```typescript theme={null}
sock.ev.on('group-participants.update', (update) => {
    console.log('Group:', update.id)
    console.log('Action:', update.action) // 'add' | 'remove' | 'promote' | 'demote'
    console.log('Participants:', update.participants)
    console.log('By:', update.author)
})
```

### Presence Events

#### presence.update

User's online/offline/typing status changed.

```typescript theme={null}
// First, subscribe to presence updates for a chat
await sock.presenceSubscribe(jid)

// Then listen for updates
sock.ev.on('presence.update', ({ id, presences }) => {
    console.log('Presence update in:', id)
    
    for (const [participant, presence] of Object.entries(presences)) {
        console.log(`${participant}:`, presence.lastKnownPresence)
        // 'available' | 'unavailable' | 'composing' | 'recording'
    }
})
```

### Call Events

#### call

Incoming or outgoing call events.

```typescript theme={null}
sock.ev.on('call', (calls) => {
    for (const call of calls) {
        console.log('Call from:', call.from)
        console.log('Call ID:', call.id)
        console.log('Status:', call.status) // 'offer' | 'ringing' | 'timeout'
        console.log('IsVideo:', call.isVideo)
        
        // Reject call
        if (call.status === 'offer') {
            await sock.rejectCall(call.id, call.from)
        }
    }
})
```

### History Events

#### messaging-history.set

Received chat history after initial connection.

```typescript theme={null}
sock.ev.on('messaging-history.set', ({ chats, contacts, messages, isLatest, progress, syncType }) => {
    console.log('History sync type:', syncType)
    console.log('Chats:', chats.length)
    console.log('Contacts:', contacts.length)  
    console.log('Messages:', messages.length)
    console.log('Is latest:', isLatest)
    console.log('Progress:', progress)
})
```

## Complete Event Handler Example

From the official example.ts:

```typescript theme={null}
import makeWASocket, { 
    DisconnectReason, 
    useMultiFileAuthState,
    makeCacheableSignalKeyStore
} from '@whiskeysockets/baileys'
import { Boom } from '@hapi/boom'
import P from 'pino'

const logger = P({ level: 'trace' })

async function startSock() {
    const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info')
    
    const sock = makeWASocket({
        logger,
        auth: {
            creds: state.creds,
            keys: makeCacheableSignalKeyStore(state.keys, logger),
        },
        generateHighQualityLinkPreview: true
    })
    
    // Batch event processing (recommended)
    sock.ev.process(async (events) => {
        // Connection updates
        if (events['connection.update']) {
            const update = events['connection.update']
            const { connection, lastDisconnect, qr } = update
            
            if (connection === 'close') {
                if ((lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) {
                    startSock()
                } else {
                    logger.fatal('Connection closed. You are logged out.')
                }
            }
            
            logger.debug(update, 'connection update')
        }
        
        // Save credentials
        if (events['creds.update']) {
            await saveCreds()
        }
        
        // New messages
        if (events['messages.upsert']) {
            const { messages, type } = events['messages.upsert']
            logger.debug({ count: messages.length, type }, 'messages.upsert')
        }
        
        // Message updates
        if (events['messages.update']) {
            logger.debug(events['messages.update'], 'messages.update')
        }
        
        // Message receipts
        if (events['message-receipt.update']) {
            logger.debug(events['message-receipt.update'])
        }
        
        // Reactions
        if (events['messages.reaction']) {
            logger.debug(events['messages.reaction'])
        }
        
        // Presence
        if (events['presence.update']) {
            logger.debug(events['presence.update'])
        }
        
        // Chats
        if (events['chats.update']) {
            logger.debug(events['chats.update'])
        }
        
        // Contacts
        if (events['contacts.update']) {
            for (const contact of events['contacts.update']) {
                if (typeof contact.imgUrl !== 'undefined') {
                    const newUrl = contact.imgUrl === null
                        ? null
                        : await sock.profilePictureUrl(contact.id!).catch(() => null)
                    logger.debug({ id: contact.id, newUrl }, 'contact profile pic updated')
                }
            }
        }
        
        // Chat deletions
        if (events['chats.delete']) {
            logger.debug('chats deleted', events['chats.delete'])
        }
        
        // Calls
        if (events['call']) {
            logger.debug(events['call'], 'call event')
        }
        
        // History
        if (events['messaging-history.set']) {
            const { chats, contacts, messages, isLatest, progress, syncType } = events['messaging-history.set']
            logger.debug({
                contacts: contacts.length,
                chats: chats.length,
                messages: messages.length,
                isLatest,
                progress,
                syncType: syncType?.toString()
            }, 'messaging-history.set')
        }
    })
    
    return sock
}

startSock()
```

## Event Summary on First Connection

<Steps>
  <Step title="connection.update (connecting)">
    Socket begins establishing connection to WhatsApp servers.
  </Step>

  <Step title="connection.update (qr or pairing)">
    QR code or pairing code available for authentication.
  </Step>

  <Step title="connection.update (open)">
    Connection successfully opened.
  </Step>

  <Step title="messaging-history.set">
    Chat history, contacts, and messages are synced.
  </Step>

  <Step title="chats.upsert / contacts.upsert">
    Individual chats and contacts are upserted.
  </Step>
</Steps>

## Best Practices

<Steps>
  <Step title="Use Batch Processing">
    Prefer `sock.ev.process()` over individual `sock.ev.on()` calls for better performance.
  </Step>

  <Step title="Always Save Credentials">
    Listen to `creds.update` and save immediately to prevent authentication issues.
  </Step>

  <Step title="Loop Through Arrays">
    Always use `for` loops when handling arrays in events like `messages.upsert`.
  </Step>

  <Step title="Check Message Content">
    Always verify message content exists before processing to avoid null/undefined errors.
  </Step>

  <Step title="Handle Reconnections">
    Implement proper reconnection logic in `connection.update` that distinguishes between logout and network failures.
  </Step>
</Steps>

## All Available Events

Here's a complete list of all events in BaileysEventMap:

| Event                       | Description                        |
| --------------------------- | ---------------------------------- |
| `connection.update`         | Connection state changed           |
| `creds.update`              | Authentication credentials updated |
| `messaging-history.set`     | History sync received              |
| `messages.upsert`           | New messages received              |
| `messages.update`           | Message metadata updated           |
| `messages.delete`           | Messages deleted                   |
| `messages.reaction`         | Reaction added/removed             |
| `message-receipt.update`    | Delivery/read receipt updated      |
| `chats.upsert`              | New chats added                    |
| `chats.update`              | Chat metadata updated              |
| `chats.delete`              | Chats deleted                      |
| `contacts.upsert`           | New contacts added                 |
| `contacts.update`           | Contact info updated               |
| `groups.upsert`             | New groups discovered              |
| `groups.update`             | Group metadata updated             |
| `group-participants.update` | Group participants changed         |
| `presence.update`           | User presence changed              |
| `call`                      | Call received/status changed       |
| `labels.edit`               | Label edited                       |
| `labels.association`        | Label association changed          |
| `blocklist.set`             | Block list set                     |
| `blocklist.update`          | Block list updated                 |

## Next Steps

<CardGroup cols={2}>
  <Card title="Session Management" icon="floppy-disk" href="/guides/session-management">
    Implement getMessage for retry handling
  </Card>

  <Card title="Sending Messages" icon="paper-plane" href="/messaging/sending-messages">
    Learn how to send messages
  </Card>

  <Card title="Socket Configuration" icon="gear" href="/guides/socket-configuration">
    Configure getMessage and other options
  </Card>
</CardGroup>
