> ## 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.

# Event System

> Understanding Baileys event system, BaileysEventMap, and event handling patterns

## Overview

Baileys uses an EventEmitter-based system to notify your application of WhatsApp events like new messages, connection changes, and contact updates. All events are strongly typed through the `BaileysEventMap` interface.

## BaileysEventMap

From `src/Types/Events.ts:20`:

```typescript theme={null}
type BaileysEventMap = {
  // Connection events
  'connection.update': Partial<ConnectionState>
  'creds.update': Partial<AuthenticationCreds>
  
  // Message events
  'messages.upsert': { 
    messages: WAMessage[]; 
    type: MessageUpsertType;
    requestId?: string 
  }
  'messages.update': WAMessageUpdate[]
  'messages.delete': { keys: WAMessageKey[] } | { jid: string; all: true }
  'messages.reaction': { key: WAMessageKey; reaction: proto.IReaction }[]
  'messages.media-update': { 
    key: WAMessageKey; 
    media?: { ciphertext: Uint8Array; iv: Uint8Array }; 
    error?: Boom 
  }[]
  
  // Chat events
  'chats.upsert': Chat[]
  'chats.update': ChatUpdate[]
  'chats.delete': string[]
  'chats.lock': { id: string; locked: boolean }
  
  // Contact events
  'contacts.upsert': Contact[]
  'contacts.update': Partial<Contact>[]
  
  // History sync
  'messaging-history.set': {
    chats: Chat[]
    contacts: Contact[]
    messages: WAMessage[]
    isLatest?: boolean
    progress?: number | null
    syncType?: proto.HistorySync.HistorySyncType | null
  }
  
  // Presence
  'presence.update': { 
    id: string; 
    presences: { [participant: string]: PresenceData } 
  }
  
  // Groups
  'groups.upsert': GroupMetadata[]
  'groups.update': Partial<GroupMetadata>[]
  'group-participants.update': {
    id: string
    author: string
    participants: GroupParticipant[]
    action: ParticipantAction
  }
  'group.join-request': {
    id: string
    author: string
    participant: string
    action: RequestJoinAction
    method: RequestJoinMethod
  }
  'group.member-tag.update': {
    groupId: string
    participant: string
    label: string
    messageTimestamp?: number
  }
  
  // Calls
  'call': WACallEvent[]
  
  // Labels
  'labels.edit': Label
  'labels.association': { 
    association: LabelAssociation; 
    type: 'add' | 'remove' 
  }
  
  // Receipts
  'message-receipt.update': MessageUserReceiptUpdate[]
  
  // Blocklist
  'blocklist.set': { blocklist: string[] }
  'blocklist.update': { blocklist: string[]; type: 'add' | 'remove' }
  
  // Settings
  'settings.update': /* various setting types */
}
```

## Event Emitter Interface

From `src/Types/Events.ts:154`:

```typescript theme={null}
interface BaileysEventEmitter {
  on<T extends keyof BaileysEventMap>(
    event: T, 
    listener: (arg: BaileysEventMap[T]) => void
  ): void
  
  off<T extends keyof BaileysEventMap>(
    event: T, 
    listener: (arg: BaileysEventMap[T]) => void
  ): void
  
  removeAllListeners<T extends keyof BaileysEventMap>(event: T): void
  
  emit<T extends keyof BaileysEventMap>(
    event: T, 
    arg: BaileysEventMap[T]
  ): boolean
}
```

## Basic Event Handling

### Single Event Listener

```typescript theme={null}
const sock = makeWASocket()

sock.ev.on('messages.upsert', ({ messages, type }) => {
  console.log('Received messages:', messages)
  console.log('Type:', type) // 'notify' | 'append'
})
```

### Multiple Event Listeners

```typescript theme={null}
sock.ev.on('connection.update', (update) => {
  console.log('Connection update:', update)
})

sock.ev.on('creds.update', async () => {
  await saveCreds()
})

sock.ev.on('chats.upsert', (chats) => {
  console.log('New chats:', chats)
})
```

## Event Processing Pattern

Baileys provides `ev.process()` for batch event processing.

From `Example/example.ts:70`:

```typescript theme={null}
sock.ev.process(
  async (events) => {
    // Connection updates
    if (events['connection.update']) {
      const update = events['connection.update']
      const { connection, lastDisconnect, qr } = update
      
      if (connection === 'close') {
        const shouldReconnect = 
          (lastDisconnect?.error as Boom)?.output?.statusCode 
            !== DisconnectReason.loggedOut
        
        if (shouldReconnect) {
          startSock()
        }
      }
      
      if (qr) {
        console.log('QR Code:', qr)
      }
    }
    
    // Credential updates
    if (events['creds.update']) {
      await saveCreds()
    }
    
    // New messages
    if (events['messages.upsert']) {
      const { messages, type } = events['messages.upsert']
      
      for (const msg of messages) {
        console.log('Message:', msg)
      }
    }
    
    // Message updates
    if (events['messages.update']) {
      for (const { key, update } of events['messages.update']) {
        if (update.pollUpdates) {
          // Handle poll vote updates
        }
      }
    }
    
    // History sync
    if (events['messaging-history.set']) {
      const { chats, contacts, messages, isLatest } = 
        events['messaging-history.set']
      
      console.log(
        `Received ${messages.length} messages, `,
        `${chats.length} chats, `,
        `${contacts.length} contacts`
      )
    }
  }
)
```

<Tip>
  Using `ev.process()` is more efficient than individual listeners when handling multiple related events, as it batches updates together.
</Tip>

## Message Events

### messages.upsert

Fired when new messages arrive or are added to history:

```typescript theme={null}
sock.ev.on('messages.upsert', ({ messages, type, requestId }) => {
  // type = 'notify' - new message from WhatsApp
  // type = 'append' - message from history sync
  // requestId - if message was requested via placeholder resync
  
  for (const msg of messages) {
    const text = msg.message?.conversation || 
                 msg.message?.extendedTextMessage?.text
    
    if (text && !msg.key.fromMe) {
      console.log('Received:', text, 'from', msg.key.remoteJid)
      
      // Reply to message
      await sock.sendMessage(
        msg.key.remoteJid!, 
        { text: 'Hello!' },
        { quoted: msg }
      )
    }
  }
})
```

<Warning>
  **Always use a loop** when handling `messages.upsert` as the `messages` array can contain multiple messages.
</Warning>

### messages.update

Fired when message status changes (delivered, read, deleted, edited):

```typescript theme={null}
sock.ev.on('messages.update', (updates) => {
  for (const { key, update } of updates) {
    if (update.status) {
      console.log(
        `Message ${key.id} status:`, 
        update.status // 0=ERROR, 1=PENDING, 2=SERVER_ACK, 3=DELIVERY_ACK, 4=READ, 5=PLAYED
      )
    }
    
    // Handle poll updates
    if (update.pollUpdates) {
      const pollCreation = await getMessage(key)
      if (pollCreation) {
        const votes = getAggregateVotesInPollMessage({
          message: pollCreation,
          pollUpdates: update.pollUpdates
        })
        console.log('Poll votes:', votes)
      }
    }
  }
})
```

### messages.reaction

Fired 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} reacted with:`, 
      reaction.text || '(removed)'
    )
  }
})
```

### messages.delete

Fired when messages are deleted:

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

## Chat Events

### chats.upsert

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

### chats.update

```typescript theme={null}
sock.ev.on('chats.update', (updates) => {
  for (const update of updates) {
    console.log('Chat updated:', update.id)
    if (update.unreadCount !== undefined) {
      console.log('Unread count:', update.unreadCount)
    }
  }
})
```

## Contact Events

### contacts.upsert

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

### contacts.update

From `Example/example.ts:204`:

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

## Group Events

### group-participants.update

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

### groups.update

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

## Presence Events

```typescript theme={null}
// Subscribe to presence updates
await sock.presenceSubscribe(jid)

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

## Call Events

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

## History Sync Events

From `Example/example.ts:119`:

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

## Event Order on First Connection

1. **`connection.update`** - Connection state changes to 'connecting'
2. **`connection.update`** - QR code received (if not authenticated)
3. **`connection.update`** - Connection opens
4. **`creds.update`** - Credentials updated
5. **`messaging-history.set`** - Message history received
6. **`chats.upsert`** - Chats loaded
7. **`contacts.upsert`** - Contacts loaded
8. **`connection.update`** - `receivedPendingNotifications: true`

## Removing Event Listeners

```typescript theme={null}
const messageHandler = ({ messages }) => {
  console.log('Messages:', messages)
}

// Add listener
sock.ev.on('messages.upsert', messageHandler)

// Remove specific listener
sock.ev.off('messages.upsert', messageHandler)

// Remove all listeners for an event
sock.ev.removeAllListeners('messages.upsert')
```

## Best Practices

<Tip>
  **Event Handling Best Practices:**

  1. **Use `ev.process()`** - Batch related events for efficiency
  2. **Handle errors** - Wrap handlers in try-catch blocks
  3. **Async handlers** - Use async/await for async operations
  4. **Loop messages** - Always iterate `messages` array in `messages.upsert`
  5. **Save credentials** - Always handle `creds.update` to save auth state
  6. **Avoid blocking** - Don't block event handlers with long operations
  7. **Clean up listeners** - Remove listeners when done to prevent memory leaks
</Tip>

## Error Handling

```typescript theme={null}
sock.ev.process(async (events) => {
  try {
    if (events['messages.upsert']) {
      const { messages } = events['messages.upsert']
      // Handle messages
    }
  } catch (error) {
    console.error('Error handling events:', error)
  }
})
```

## TypeScript Type Safety

Baileys provides full TypeScript support for events:

```typescript theme={null}
// ✅ Type-safe event handling
sock.ev.on('messages.upsert', ({ messages, type }) => {
  // messages: WAMessage[]
  // type: MessageUpsertType
})

// ❌ TypeScript will error on invalid events
sock.ev.on('invalid.event', (data) => {
  // Error: Argument of type '"invalid.event"' is not assignable...
})
```

## See Also

* [Connection Lifecycle](/concepts/connection) - Connection events
* [Authentication](/concepts/authentication) - Credential events
* [Message Store](/concepts/message-store) - Storing event data
