Skip to main content
Baileys uses an EventEmitter-based system to notify your application of WhatsApp events like incoming messages, connection changes, and contact updates.

Event system overview

All events are emitted through the sock.ev event emitter with full TypeScript typing:
import makeWASocket from '@whiskeysockets/baileys'

const sock = makeWASocket({ /* config */ })

sock.ev.on('messages.upsert', ({ messages }) => {
    console.log('Received messages:', messages)
})

BaileysEventMap

All available events are defined in the BaileysEventMap type:
BaileysEventMap
export type BaileysEventMap = {
    'connection.update': Partial<ConnectionState>
    'creds.update': Partial<AuthenticationCreds>
    'messaging-history.set': { /* ... */ }
    'chats.upsert': Chat[]
    'chats.update': ChatUpdate[]
    'chats.delete': string[]
    'presence.update': { /* ... */ }
    'contacts.upsert': Contact[]
    'contacts.update': Partial<Contact>[]
    'messages.delete': { /* ... */ }
    'messages.update': WAMessageUpdate[]
    'messages.media-update': { /* ... */ }[]
    'messages.upsert': { /* ... */ }
    'messages.reaction': { /* ... */ }[]
    'message-receipt.update': MessageUserReceiptUpdate[]
    'groups.upsert': GroupMetadata[]
    'groups.update': Partial<GroupMetadata>[]
    'group-participants.update': { /* ... */ }
    'call': WACallEvent[]
    // ... and more
}
View the complete event map in the Baileys documentation.

Core events

Connection events

connection.update

Monitor connection state changes:
sock.ev.on('connection.update', (update) => {
    const { connection, lastDisconnect, qr } = update
    
    if (connection === 'open') {
        console.log('Connected to WhatsApp')
    } else if (connection === 'close') {
        console.log('Disconnected from WhatsApp')
    }
})

creds.update

Save authentication credentials when they update:
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')

sock.ev.on('creds.update', saveCreds)
Always implement creds.update to save credentials. Failing to save updated credentials will cause message delivery failures and session issues.

Message events

messages.upsert

Receive new or updated messages:
sock.ev.on('messages.upsert', ({ messages, type }) => {
    for (const msg of messages) {
        // Only handle new messages (not history)
        if (type === 'notify') {
            console.log('New message:', msg)
            
            const text = msg.message?.conversation || 
                        msg.message?.extendedTextMessage?.text
            
            if (text && !msg.key.fromMe) {
                console.log('Message from', msg.key.remoteJid, ':', text)
            }
        }
    }
})
New message received in real-time while connected.
if (type === 'notify') {
    // Handle new incoming message
}
Always use a loop to process messages: for (const message of event.messages). The array can contain multiple messages.

messages.update

Handle message updates (edits, deletions, status changes):
sock.ev.on('messages.update', (updates) => {
    for (const { key, update } of updates) {
        console.log('Message', key.id, 'updated:', update)
        
        // Handle poll updates
        if (update.pollUpdates) {
            const pollCreation = await getMessage(key)
            if (pollCreation) {
                const aggregated = getAggregateVotesInPollMessage({
                    message: pollCreation,
                    pollUpdates: update.pollUpdates
                })
                console.log('Poll results:', aggregated)
            }
        }
    }
})

messages.reaction

Handle message reactions:
sock.ev.on('messages.reaction', (reactions) => {
    for (const { key, reaction } of reactions) {
        console.log(
            'Reaction', reaction.text,
            'on message', key.id,
            'from', reaction.key
        )
    }
})

messages.delete

Handle deleted messages:
sock.ev.on('messages.delete', (deletion) => {
    if ('keys' in deletion) {
        for (const key of deletion.keys) {
            console.log('Message deleted:', key.id)
        }
    } else if (deletion.all) {
        console.log('All messages deleted in chat:', deletion.jid)
    }
})

Chat events

chats.upsert

New chats created:
sock.ev.on('chats.upsert', (chats) => {
    for (const chat of chats) {
        console.log('New chat:', chat.id)
    }
})

chats.update

Existing chats updated:
sock.ev.on('chats.update', (updates) => {
    for (const update of updates) {
        console.log('Chat updated:', update.id, update)
    }
})

chats.delete

Chats deleted:
sock.ev.on('chats.delete', (deletedChatIds) => {
    for (const id of deletedChatIds) {
        console.log('Chat deleted:', id)
    }
})

Contact events

contacts.upsert

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

contacts.update

Contact information updated:
sock.ev.on('contacts.update', (updates) => {
    for (const contact of updates) {
        if (typeof contact.imgUrl !== 'undefined') {
            console.log('Contact profile picture updated:', contact.id)
        }
    }
})

Group events

groups.upsert

New groups created or joined:
sock.ev.on('groups.upsert', (groups) => {
    for (const group of groups) {
        console.log('New group:', group.id, group.subject)
    }
})

groups.update

Group metadata updated:
sock.ev.on('groups.update', (updates) => {
    for (const update of updates) {
        console.log('Group updated:', update.id, update)
    }
})

group-participants.update

Group participant changes:
sock.ev.on('group-participants.update', (event) => {
    console.log(
        'Participants', event.action,
        'in group', event.id,
        ':', event.participants
    )
})
Participants added to group
if (event.action === 'add') {
    console.log('Added:', event.participants)
}

Presence events

presence.update

User presence updates (online, typing, etc.):
// Subscribe to presence updates for a chat
await sock.presenceSubscribe(jid)

sock.ev.on('presence.update', ({ id, presences }) => {
    console.log('Presence update in', id)
    
    for (const [participant, presence] of Object.entries(presences)) {
        console.log(participant, 'is', presence.lastKnownPresence)
    }
})

Call events

call

Incoming or outgoing call events:
sock.ev.on('call', (calls) => {
    for (const call of calls) {
        console.log('Call from', call.from, ':', call.status)
        
        if (call.status === 'offer') {
            // Reject incoming call
            await sock.rejectCall(call.id, call.from)
        }
    }
})

History sync events

messaging-history.set

Receive message history during initial sync:
sock.ev.on('messaging-history.set', ({ chats, contacts, messages, isLatest }) => {
    console.log(
        'Received history:',
        messages.length, 'messages,',
        chats.length, 'chats,',
        contacts.length, 'contacts'
    )
    
    if (isLatest) {
        console.log('This is the latest history')
    }
})

Event processing

Baileys provides an efficient batch event processing API:
sock.ev.process(async (events) => {
    // All events are provided in a map
    if (events['connection.update']) {
        const update = events['connection.update']
        // Handle connection update
    }
    
    if (events['messages.upsert']) {
        const { messages } = events['messages.upsert']
        // Handle messages
    }
    
    if (events['creds.update']) {
        await saveCreds()
    }
})
The process function allows you to handle multiple event types efficiently in a single batch. This is the recommended approach from the example.ts file.

Complete event handling example

Here’s a comprehensive example handling common events:
import makeWASocket, { 
    DisconnectReason, 
    useMultiFileAuthState 
} from '@whiskeysockets/baileys'
import { Boom } from '@hapi/boom'

async function connectToWhatsApp() {
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
    
    const sock = makeWASocket({
        auth: state,
        printQRInTerminal: true
    })
    
    sock.ev.process(async (events) => {
        // Connection updates
        if (events['connection.update']) {
            const { connection, lastDisconnect } = events['connection.update']
            
            if (connection === 'close') {
                const shouldReconnect = 
                    (lastDisconnect?.error as Boom)?.output?.statusCode !== 
                    DisconnectReason.loggedOut
                
                if (shouldReconnect) {
                    connectToWhatsApp()
                }
            } else if (connection === 'open') {
                console.log('Connected successfully')
            }
        }
        
        // Save credentials
        if (events['creds.update']) {
            await saveCreds()
        }
        
        // Handle new messages
        if (events['messages.upsert']) {
            const { messages, type } = events['messages.upsert']
            
            if (type === 'notify') {
                for (const msg of messages) {
                    const text = msg.message?.conversation || 
                                msg.message?.extendedTextMessage?.text
                    
                    if (text && !msg.key.fromMe) {
                        console.log('Received:', text)
                        
                        // Reply to message
                        await sock.sendMessage(
                            msg.key.remoteJid!,
                            { text: 'Hello!' }
                        )
                    }
                }
            }
        }
        
        // Handle message updates
        if (events['messages.update']) {
            for (const { key, update } of events['messages.update']) {
                if (update.pollUpdates) {
                    console.log('Poll updated:', key.id)
                }
            }
        }
        
        // Handle group updates
        if (events['group-participants.update']) {
            const event = events['group-participants.update']
            console.log(
                event.participants, event.action,
                'in group', event.id
            )
        }
    })
    
    return sock
}

connectToWhatsApp()

Event handling best practices

1

Use event.process for batch handling

Process multiple events efficiently in a single function:
sock.ev.process(async (events) => {
    // Handle multiple event types
})
2

Always save credentials

Implement creds.update to prevent session issues:
if (events['creds.update']) {
    await saveCreds()
}
3

Loop through message arrays

Always iterate over message arrays:
for (const msg of messages) {
    // Process each message
}
4

Check message type

Filter for ‘notify’ type to handle only new messages:
if (type === 'notify') {
    // Handle real-time messages
}

Next steps

Data store

Implement storage for messages and chats

Sending messages

Learn how to send different types of messages

Build docs developers (and LLMs) love