Events & Chat Management

All events you can listen to, plus chat, group, newsletter, and profile functions.

Event System

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)

connection.update

The most important event. Fired on every connection state change.

Event Data

PropertyTypeWhen Fired
connection'open' | 'connecting' | 'close'Every state change
lastDisconnect.errorBoom | ErrorOn close. Check .output.statusCode
lastDisconnect.dateDateDisconnect timestamp
qrstring | undefinedQR code (Base64) during QR pairing
isNewLoginbooleanOn successful pairing
receivedPendingNotificationsbooleanWhen offline notifications are delivered
isOnlinebooleanOnline/offline status
reachoutTimeLockReachoutTimelockStateAccount restriction info

Error Codes & Reconnect Logic

lastDisconnect.error.output.statusCode (when using Boom):

CodeConstantMeaningReconnect?
401DisconnectReason.loggedOutSession expired / logged outNo. Must re-pair
403DisconnectReason.forbiddenAccount banned / restrictedNo
408DisconnectReason.timedOut / connectionLostConnection timeoutYes
428DisconnectReason.connectionClosedConnection closed by serverYes
440DisconnectReason.connectionReplacedAnother device connectedOptional
500DisconnectReason.badSessionCorrupt sessionNo. Delete auth & re-pair
503DisconnectReason.unavailableServiceServer overloadedYes (longer delay)
515DisconnectReason.restartRequiredServer 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)
  }
})

messages.upsert

Main event for receiving & reading messages. Fires for new messages — both from others and yourself (when emitOwnEvents: true).

Event Data

PropertyTypeDescription
messagesWAMessage[]Array of messages (usually 1 or more)
type'notify' | 'append'notify = real-time. append = history sync
requestIdstring | undefinedPresent when fetched from phone due to server unavailability

How to Read Messages

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)
  }
})

Filter Own Messages

// Skip if fromMe
if (msg.key.fromMe) return

// Or only process fromMe
if (!msg.key.fromMe) return

messages.update

Fired when an existing message changes: read status, edit, poll vote update, etc.

Event Data

PropertyTypeDescription
keyWAMessageKeyKey of the updated message
updatePartial<WAMessage>Fields that changed

Poll Updates

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)
  }
})

messages.reaction

Fired when someone adds or removes a reaction.

Event Data

PropertyTypeDescription
keyWAMessageKeyKey of the reacted message
reaction.textstring | nullReaction emoji. null = reaction removed
reaction.keyWAMessageKeyKey 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}`)
  }
})

messages.delete

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)
  }
})

message-receipt.update

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}`)
  }
})

group-participants.update

Fired when group members change: someone joins, is removed, promoted, or demoted.

Event Data

PropertyTypeDescription
idstringGroup JID
authorstringWho performed the action
participantsstring[]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)
  }
})

group.join-request

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}`)
})

presence.update

Online/typing/recording presence updates from other users.

Event Data

PropertyTypeDescription
idstringChat JID
presences{ [participant]: PresenceData }Map of participant to their presence

PresenceData

FieldTypeDescription
lastKnownPresence'available' | 'unavailable' | 'composing' | 'recording' | 'paused'Presence status
lastSeennumber | undefinedLast online timestamp
groupOnlineCountnumber | undefinedOnline 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}`)
  }
})

call

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)
  }
})

chats.upsert / chats.update / chats.delete

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)
})

contacts.upsert / contacts.update

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)
  }
})

creds.update

Credential updates — Signal keys, pairing, sync state. MUST be saved.

sock.ev.on('creds.update', saveCreds)

blocklist.set / blocklist.update

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)
})

Other Events

messaging-history.set

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`)
})

messaging-history.status

History sync progress status.

sock.ev.on('messaging-history.status', ({ syncType, status, explicit }) => {
  console.log(`Sync ${syncType}: ${status} (explicit: ${explicit})`)
})

lid-mapping.update

LID to PN mapping updated.

messages.media-update

Media update — used internally by updateMediaMessage.

labels.edit / labels.association

Label change events.

settings.update

Setting changes: unarchiveChats, locale, disableLinkPreviews, etc.

chats.lock

Chat locked / unlocked.

message-capping.update

Message send limit info (when hitting spam limit).


Chat Management

readMessages

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.

sendPresenceUpdate

Send presence status (online, typing, recording).

Parameters

TypeDescription
'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

chatModify

General function for chat modifications: archive, mute, pin, delete, star, mark unread, etc.

Usage

// 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)

Profile Functions

updateProfilePicture

// 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 })

removeProfilePicture

await sock.removeProfilePicture(jid)

updateProfileStatus

await sock.updateProfileStatus('My new status')

updateProfileName

await sock.updateProfileName('New Name')

profilePictureUrl

const url = await sock.profilePictureUrl('628xxx@s.whatsapp.net', 'image')     // HQ
const preview = await sock.profilePictureUrl('628xxx@s.whatsapp.net', 'preview')  // LQ

Privacy Settings

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)

Group Functions

groupCreate

const group = await sock.groupCreate('Group Name', [
  '628xxx@s.whatsapp.net',
  '628yyy@s.whatsapp.net',
])
console.log('Group ID:', group.id)

groupMetadata

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)

groupParticipantsUpdate

// 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')

Group Settings

// 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

Group Invite

// 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')

Other Group Functions

// 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')

Newsletter / Channel Functions

Managing Newsletters

// 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')

Newsletter Events

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)
})
← Back Next: Advanced →