All events you can listen to, plus chat, group, newsletter, and profile functions.
All events are emitted through sock.ev (EventEmitter). Register listeners with:
sock.ev.on('event.name', async (data) => { /* handle */ })
Remove listeners with:
sock.ev.off('event.name', handler)
The most important event. Fired on every connection state change.
| Property | Type | When Fired |
|---|---|---|
connection | 'open' | 'connecting' | 'close' | Every state change |
lastDisconnect.error | Boom | Error | On close. Check .output.statusCode |
lastDisconnect.date | Date | Disconnect timestamp |
qr | string | undefined | QR code (Base64) during QR pairing |
isNewLogin | boolean | On successful pairing |
receivedPendingNotifications | boolean | When offline notifications are delivered |
isOnline | boolean | Online/offline status |
reachoutTimeLock | ReachoutTimelockState | Account restriction info |
lastDisconnect.error.output.statusCode (when using Boom):
| Code | Constant | Meaning | Reconnect? |
|---|---|---|---|
| 401 | DisconnectReason.loggedOut | Session expired / logged out | No. Must re-pair |
| 403 | DisconnectReason.forbidden | Account banned / restricted | No |
| 408 | DisconnectReason.timedOut / connectionLost | Connection timeout | Yes |
| 428 | DisconnectReason.connectionClosed | Connection closed by server | Yes |
| 440 | DisconnectReason.connectionReplaced | Another device connected | Optional |
| 500 | DisconnectReason.badSession | Corrupt session | No. Delete auth & re-pair |
| 503 | DisconnectReason.unavailableService | Server overloaded | Yes (longer delay) |
| 515 | DisconnectReason.restartRequired | Server restart (normal after pairing) | Yes |
sock.ev.on('connection.update', async ({ connection, lastDisconnect, qr, isNewLogin }) => { if (qr) console.log('Scan QR:', qr) if (isNewLogin) console.log('New login!') if (connection === 'open') console.log('Ready!') if (connection === 'close') { const code = lastDisconnect?.error?.output?.statusCode const shouldReconnect = ![401, 403, 500].includes(code) if (shouldReconnect) setTimeout(reconnect, 3000) } })
Main event for receiving & reading messages. Fires for new messages — both from others and yourself (when emitOwnEvents: true).
| Property | Type | Description |
|---|---|---|
messages | WAMessage[] | Array of messages (usually 1 or more) |
type | 'notify' | 'append' | notify = real-time. append = history sync |
requestId | string | undefined | Present when fetched from phone due to server unavailability |
sock.ev.on('messages.upsert', async ({ messages, type }) => { if (type !== 'notify') return // skip history sync for (const msg of messages) { if (msg.key.fromMe) continue // skip own messages const jid = msg.key.remoteJid const sender = msg.key.participant || jid const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text || msg.message?.imageMessage?.caption || msg.message?.videoMessage?.caption || '' console.log('From:', sender, 'Text:', text) } })
// Skip if fromMe if (msg.key.fromMe) return // Or only process fromMe if (!msg.key.fromMe) return
Fired when an existing message changes: read status, edit, poll vote update, etc.
| Property | Type | Description |
|---|---|---|
key | WAMessageKey | Key of the updated message |
update | Partial<WAMessage> | Fields that changed |
When someone votes in a poll, you get messages.update with update.message.pollUpdateMessage.
sock.ev.on('messages.update', ({ key, update }) => { // Poll vote update const pollUpdate = update.message?.pollUpdateMessage if (pollUpdate) { console.log('Poll vote:', key.id, pollUpdate.vote?.selectedOptions) } // Message edited if (update.message?.extendedTextMessage) { console.log('Message edited:', key.id) } })
Fired when someone adds or removes a reaction.
| Property | Type | Description |
|---|---|---|
key | WAMessageKey | Key of the reacted message |
reaction.text | string | null | Reaction emoji. null = reaction removed |
reaction.key | WAMessageKey | Key of the reaction message itself |
sock.ev.on('messages.reaction', ({ key, reaction }) => { if (reaction.text) { console.log(`Received ${reaction.text} on ${key.id}`) } else { console.log(`Reaction removed from ${key.id}`) } })
Fired when a message is deleted.
sock.ev.on('messages.delete', (data) => { if ('keys' in data) { console.log('Messages deleted:', data.keys.length) } else { console.log('Chat cleared:', data.jid) } })
Delivery / read receipt notifications (checkmarks).
sock.ev.on('message-receipt.update', (updates) => { for (const { key, receipt } of updates) { console.log(`Message ${key.id} read at ${receipt.readTimestamp}`) } })
Fired when group members change: someone joins, is removed, promoted, or demoted.
| Property | Type | Description |
|---|---|---|
id | string | Group JID |
author | string | Who performed the action |
participants | string[] | Who was affected |
action | 'add' | 'remove' | 'promote' | 'demote' | The action performed |
sock.ev.on('group-participants.update', ({ id, author, participants, action }) => { console.log(`[${action}] ${participants.join(', ')} by ${author} in ${id}`) if (action === 'add') { console.log('New members:', participants) } if (action === 'remove') { console.log('Members left:', participants) } if (action === 'promote') { console.log('Became admin:', participants) } })
When a group uses joinApprovalMode, this event fires when someone requests to join.
sock.ev.on('group.join-request', ({ id, author, participant, action, method }) => { console.log(`${participant} requested to join ${id}`) })
Online/typing/recording presence updates from other users.
| Property | Type | Description |
|---|---|---|
id | string | Chat JID |
presences | { [participant]: PresenceData } | Map of participant to their presence |
| Field | Type | Description |
|---|---|---|
lastKnownPresence | 'available' | 'unavailable' | 'composing' | 'recording' | 'paused' | Presence status |
lastSeen | number | undefined | Last online timestamp |
groupOnlineCount | number | undefined | Online count in group (when available) |
sock.ev.on('presence.update', ({ id, presences }) => { for (const [participant, presence] of Object.entries(presences)) { console.log(`${participant}: ${presence.lastKnownPresence}`) } })
Fired on incoming calls. Can be rejected with sock.rejectCall.
sock.ev.on('call', async (calls) => { for (const call of calls) { console.log('Call from:', call.from, 'type:', call.isVideo ? 'video' : 'audio') // Auto reject await sock.rejectCall(call.id, call.from) } })
Events for chat list changes.
sock.ev.on('chats.upsert', (chats) => { console.log('New chats:', chats.length) }) sock.ev.on('chats.update', (updates) => { for (const update of updates) { console.log('Chat updated:', update.id, update.unreadCount) } }) sock.ev.on('chats.delete', (ids) => { console.log('Chat deleted:', ids) })
Contact updates — name, photo, etc.
sock.ev.on('contacts.upsert', (contacts) => { for (const contact of contacts) { console.log('New contact:', contact.id, contact.name) } }) sock.ev.on('contacts.update', (updates) => { for (const update of updates) { console.log('Contact updated:', update.id, update.notify) } })
Credential updates — Signal keys, pairing, sync state. MUST be saved.
sock.ev.on('creds.update', saveCreds)
Block list events.
sock.ev.on('blocklist.set', ({ blocklist }) => { console.log('Blocklist:', blocklist) }) sock.ev.on('blocklist.update', ({ blocklist, type }) => { console.log(`${type === 'add' ? 'Added' : 'Removed'} from blocklist:`, blocklist) })
History sync data — chats, contacts, messages from server.
sock.ev.on('messaging-history.set', ({ chats, contacts, messages, isLatest }) => { console.log(`History: ${chats.length} chats, ${contacts.length} contacts, ${messages.length} messages`) })
History sync progress status.
sock.ev.on('messaging-history.status', ({ syncType, status, explicit }) => { console.log(`Sync ${syncType}: ${status} (explicit: ${explicit})`) })
LID to PN mapping updated.
Media update — used internally by updateMediaMessage.
Label change events.
Setting changes: unarchiveChats, locale, disableLinkPreviews, etc.
Chat locked / unlocked.
Message send limit info (when hitting spam limit).
Mark messages as read (blue checkmark). Supports bulk across multiple chats.
// Read 1 message await sock.readMessages([messageKey]) // Read multiple messages from different chats await sock.readMessages([key1, key2, key3])
Automatically uses 'read' (blue double-check) or 'read-self' (single check) depending on the user's privacy settings.
Send presence status (online, typing, recording).
| Type | Description |
|---|---|
'available' | Mark online |
'unavailable' | Mark offline |
'composing' | "typing..." status |
'recording' | "recording..." status |
'paused' | Stop typing |
await sock.sendPresenceUpdate('available') // Online await sock.sendPresenceUpdate('composing', jid) // Typing in a chat await sock.sendPresenceUpdate('recording', jid) // Recording voice note await sock.sendPresenceUpdate('paused', jid) // Stop typing await sock.sendPresenceUpdate('unavailable') // Offline
General function for chat modifications: archive, mute, pin, delete, star, mark unread, etc.
// Archive chat await sock.chatModify({ archive: true }, jid) // Unarchive await sock.chatModify({ archive: false }, jid) // Pin chat (pin order number) await sock.chatModify({ pin: 1 }, jid) // Unpin await sock.chatModify({ pin: null }, jid) // Mute chat for 8 hours (value in seconds) await sock.chatModify({ mute: 28800 }, jid) // Mute forever await sock.chatModify({ mute: -1 }, jid) // Unmute await sock.chatModify({ mute: null }, jid) // Mark as unread await sock.chatModify({ markRead: false }, jid) // Mark as read await sock.chatModify({ markRead: true }, jid) // Clear chat messages await sock.chatModify({ clear: 'all' }, jid) // Star messages await sock.star(jid, [{ id: msgKey.id, fromMe: msgKey.fromMe }], true) // Unstar messages await sock.star(jid, [{ id: msgKey.id }], false) // Delete chat await sock.chatModify({ delete: true }, jid)
// Update own profile picture await sock.updateProfilePicture(sock.user?.id, fs.readFileSync('./photo.jpg')) // Update group profile picture (requires admin) await sock.updateProfilePicture('123456@g.us', { url: './group.jpg' }, { width: 640, height: 640 })
await sock.removeProfilePicture(jid)
await sock.updateProfileStatus('My new status')
await sock.updateProfileName('New Name')
const url = await sock.profilePictureUrl('628xxx@s.whatsapp.net', 'image') // HQ const preview = await sock.profilePictureUrl('628xxx@s.whatsapp.net', 'preview') // LQ
await sock.updateLastSeenPrivacy('contacts') // 'all' | 'contacts' | 'none' await sock.updateOnlinePrivacy('contacts') // 'all' | 'contacts' await sock.updateProfilePicturePrivacy('contacts') // 'all' | 'contacts' | 'none' await sock.updateStatusPrivacy('contacts') await sock.updateReadReceiptsPrivacy('none') // 'all' | 'none' await sock.updateGroupsAddPrivacy('contacts') // 'all' | 'contacts' | 'none' await sock.updateMessagesPrivacy('all') // 'all' | 'none' await sock.updateCallPrivacy('all') // 'all' | 'contacts' | 'none' await sock.updateDefaultDisappearingMode(86400)
const group = await sock.groupCreate('Group Name', [ '628xxx@s.whatsapp.net', '628yyy@s.whatsapp.net', ]) console.log('Group ID:', group.id)
const meta = await sock.groupMetadata('123456@g.us') console.log(meta.subject, meta.size, meta.owner, meta.desc) console.log(meta.participants) // [{ id, admin, lid }] console.log(meta.restrict, meta.announce, meta.ephemeralDuration)
// Add members const result = await sock.groupParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'add') // Remove members await sock.groupParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'remove') // Make admin await sock.groupParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'promote') // Remove admin await sock.groupParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'demote')
// Change group name await sock.groupUpdateSubject('123@g.us', 'New Name') // Change description await sock.groupUpdateDescription('123@g.us', 'New description') // Remove description await sock.groupUpdateDescription('123@g.us') // Set group to admin-only messaging (announcement) await sock.groupSettingUpdate('123@g.us', 'announcement') // Open group (everyone can send) await sock.groupSettingUpdate('123@g.us', 'not_announcement') // Lock group (only admins can edit) await sock.groupSettingUpdate('123@g.us', 'locked') // Unlock await sock.groupSettingUpdate('123@g.us', 'unlocked') // Member add mode await sock.groupMemberAddMode('123@g.us', 'admin_add') // Admins only await sock.groupMemberAddMode('123@g.us', 'all_member_add') // All members // Join approval mode await sock.groupJoinApprovalMode('123@g.us', 'on') await sock.groupJoinApprovalMode('123@g.us', 'off') // Disappearing messages in group await sock.groupToggleEphemeral('123@g.us', 86400) // 24 hours await sock.groupToggleEphemeral('123@g.us', 0) // disable
// Get invite code const code = await sock.groupInviteCode('123@g.us') // Revoke / regenerate invite code const newCode = await sock.groupRevokeInvite('123@g.us') // Accept invite with code const groupJid = await sock.groupAcceptInvite('abc123') // Group info from invite code const info = await sock.groupGetInviteInfo('abc123') // Accept GroupInviteMessage (v4) await sock.groupAcceptInviteV4(msg.key, msg.message.groupInviteMessage) // Revoke invite for someone (v4) await sock.groupRevokeInviteV4('123@g.us', '628xxx@s.whatsapp.net')
// Leave group await sock.groupLeave('123@g.us') // Fetch all participating groups const allGroups = await sock.groupFetchAllParticipating() console.log(Object.keys(allGroups)) // List join requests const requests = await sock.groupRequestParticipantsList('123@g.us') // Approve / reject join requests await sock.groupRequestParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'approve')
// Create newsletter const nl = await sock.newsletterCreate('Channel Name', 'Channel description') // Get metadata const meta = await sock.newsletterMetadata('jid', '123@newsletter') const metaByInvite = await sock.newsletterMetadata('invite', 'invitecode') // Follow / unfollow await sock.newsletterFollow('123@newsletter') await sock.newsletterUnfollow('123@newsletter') // Mute / unmute await sock.newsletterMute('123@newsletter') await sock.newsletterUnmute('123@newsletter') // Update name, description await sock.newsletterUpdateName('123@newsletter', 'New Name') await sock.newsletterUpdateDescription('123@newsletter', 'New description') // Update picture await sock.newsletterUpdatePicture('123@newsletter', fs.readFileSync('./photo.jpg')) await sock.newsletterRemovePicture('123@newsletter') // Delete newsletter await sock.newsletterDelete('123@newsletter') // Count subscribers const { subscribers } = await sock.newsletterSubscribers('123@newsletter') // Admin count const adminCount = await sock.newsletterAdminCount('123@newsletter') // React to newsletter message await sock.newsletterReactMessage('123@newsletter', 'server_id', '🔥') await sock.newsletterReactMessage('123@newsletter', 'server_id') // remove reaction // Fetch newsletter messages const messages = await sock.newsletterFetchMessages('123@newsletter', 50, 0, 0) // Subscribe to live updates (get real-time notifications) const live = await sock.subscribeNewsletterUpdates('123@newsletter') // Change owner / demote admin await sock.newsletterChangeOwner('123@newsletter', '628xxx@s.whatsapp.net') await sock.newsletterDemote('123@newsletter', '628xxx@s.whatsapp.net')
sock.ev.on('newsletter.reaction', ({ id, server_id, reaction }) => { console.log('Newsletter reaction:', id, reaction) }) sock.ev.on('newsletter.view', ({ id, server_id, count }) => { console.log('Viewed:', count, 'times') }) sock.ev.on('newsletter-participants.update', ({ id, author, user, new_role, action }) => { console.log(`${user} ${action} as ${new_role}`) }) sock.ev.on('newsletter-settings.update', ({ id, update }) => { console.log('Newsletter settings changed', id) })