@zervonex/baileys
Advanced WhatsApp Web API — Built on WhiskeySockets/Baileys
*Modern, feature-rich, and developer-friendly WhatsApp automation library*
Formerly known as @nexustechpro/baileys
⚠️ This is an independent fork of @whiskeysockets/baileys.
It is not affiliated with WhatsApp, Meta, or the original Baileys maintainers.
📋 Table of Contents
📦 Installation
npm install @zervonex/baileys
yarn add @zervonex/baileys
Drop-in replacement for @whiskeysockets/baileys — add this to package.json:
{
"dependencies": {
"@zervonex/baileys": "latest"
}
}
Import:
import makeWASocket from '@zervonex/baileys'
const { default: makeWASocket } = require('@zervonex/baileys')
🚀 Quick Start
import makeWASocket, {
useMultiFileAuthState,
DisconnectReason,
makeCacheableSignalKeyStore,
Browsers
} from '@zervonex/baileys'
import pino from 'pino'
const connectToWhatsApp = async () => {
const { state, saveCreds } = await useMultiFileAuthState('auth_session')
const logger = pino({ level: 'silent' })
const sock = makeWASocket({
auth: {
creds: state.creds,
keys: makeCacheableSignalKeyStore(state.keys, logger)
},
browser: Browsers.ubuntu('Chrome'),
generateHighQualityLinkPreview: true,
logger
})
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
if (connection === 'close') {
const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut
if (shouldReconnect) connectToWhatsApp()
} else if (connection === 'open') {
console.log('✅ Connected!')
}
})
sock.ev.on('creds.update', saveCreds)
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
if (!msg.key.fromMe && msg.message) {
await sock.sendMessage(msg.key.remoteJid, { text: 'Hello!' })
}
}
})
}
connectToWhatsApp()
🔐 Authentication
Pairing Code (Recommended)
No QR scanning — just a phone number. Call requestPairingCode directly after creating the socket. The socket internally waits for the WebSocket to be ready before sending the request, so no manual polling or delays needed on your end.
import makeWASocket, {
useMultiFileAuthState,
makeCacheableSignalKeyStore,
Browsers
} from '@zervonex/baileys'
import pino from 'pino'
const { state, saveCreds } = await useMultiFileAuthState('auth_session')
const logger = pino({ level: 'silent' })
const sock = makeWASocket({
auth: {
creds: state.creds,
keys: makeCacheableSignalKeyStore(state.keys, logger)
},
browser: Browsers.ubuntu('Chrome'),
logger
})
sock.ev.on('creds.update', saveCreds)
if (!sock.authState.creds.registered) {
const code = await sock.requestPairingCode('1234567890')
console.log('Pairing code:', code)
}
Custom Pairing Code
const code = await sock.requestPairingCode('1234567890', 'NEXUS001')
console.log('Custom code:', code)
QR Code
Scan the QR with your WhatsApp mobile app under Linked Devices.
import makeWASocket, {
useMultiFileAuthState,
makeCacheableSignalKeyStore,
Browsers
} from '@zervonex/baileys'
import qrcode from 'qrcode-terminal'
import pino from 'pino'
const { state, saveCreds } = await useMultiFileAuthState('auth_session')
const logger = pino({ level: 'silent' })
const sock = makeWASocket({
auth: {
creds: state.creds,
keys: makeCacheableSignalKeyStore(state.keys, logger)
},
browser: Browsers.ubuntu('Chrome'),
logger
})
sock.ev.on('connection.update', ({ connection, qr, lastDisconnect }) => {
if (qr) qrcode.generate(qr, { small: true })
if (connection === 'open') console.log('✅ Connected!')
if (connection === 'close') {
const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut
if (shouldReconnect) connectToWhatsApp()
}
})
sock.ev.on('creds.update', saveCreds)
Install the QR renderer: npm install qrcode-terminal
🌐 Browser Options
The browser config tells WhatsApp what client you appear to be. This affects how WhatsApp treats your session and which features it enables.
Browsers.ubuntu('Chrome') | ['Ubuntu', 'Chrome', '22.04.4'] | ✅ Recommended — stable, widely used |
Browsers.macOS('Safari') | ['Mac OS', 'Safari', '14.4.1'] | Good alternative |
Browsers.windows('Chrome') | ['Windows', 'Chrome', '10.0.22631'] | Works well |
Browsers.baileys('Desktop') | ['Baileys', 'Desktop', '6.5.0'] | Identifies as Baileys explicitly |
Browsers.appropriate('Chrome') | Auto-detects your OS | Uses your actual platform |
import { Browsers } from '@zervonex/baileys'
const sock = makeWASocket({
browser: Browsers.ubuntu('Chrome')
})
⚙️ Socket Configuration
Key options from DEFAULT_CONNECTION_CONFIG:
const sock = makeWASocket({
auth: { creds, keys },
browser: Browsers.ubuntu('Chrome'),
generateHighQualityLinkPreview: true,
cachedGroupMetadata: async (jid) => groupCache.get(jid),
markOnlineOnConnect: false,
syncFullHistory: false,
aiLabel: false,
logger: pino({ level: 'silent' }),
connectTimeoutMs: 60000,
maxMsgRetryCount: 5,
getMessage: async (key) => store.loadMessage(key.remoteJid, key.id),
autoFollowNewsletter: false,
})
💾 Session Management
Two adapters are available. They do not share an identical return shape — see the differences noted below each.
File-based (default)
Atomic writes, sha256 checksum integrity, per-file mutex locking, corrupt file recovery, and legacy file upgrade. Best for single-instance bots or local development.
import { useMultiFileAuthState } from '@zervonex/baileys'
const { state, saveCreds, getStats } = await useMultiFileAuthState('auth_session', {
preKeyRetention: 150,
cleanupThreshold: 50,
logger,
})
const sock = makeWASocket({ auth: state })
sock.ev.on('creds.update', saveCreds)
const stats = await getStats()
To fully log out and delete a session's files, remove the session folder directly (e.g. fs.rm(folder, { recursive: true, force: true })) — there is no clearSession/close method on this adapter.
Keyv-based (production)
Universal adapter for any database backend via Keyv. Pass a connection string — the matching backend driver is resolved and imported automatically, no manual adapter imports needed.
Storage format matches useMultiFileAuthState exactly: every value is wrapped as { data, __checksum } with a sha256 integrity check, so corruption is detected and self-healed the same way on both adapters. Reads also auto-detect and repair values that were written by an older/buggy version of this adapter (including arbitrarily nested { value: { value: ... } } wrapping) — no manual data-fixing needed if you're upgrading from an earlier release.
The first argument is the session ID — equivalent to the folder name in useMultiFileAuthState. It's used as a key prefix (sessionId::key), so multiple bots can safely share one backend. The second argument is a connection string, a pre-built Keyv instance, or a raw KeyvStoreAdapter:
import { useKeyvAuthState } from '@zervonex/baileys'
const { state, saveCreds } = await useKeyvAuthState('bot1', 'redis://localhost:6379')
const { state, saveCreds } = await useKeyvAuthState('bot1', 'postgresql://user:pass@localhost/db')
const { state, saveCreds } = await useKeyvAuthState('bot1', 'mongodb://localhost/baileys')
const { state, saveCreds } = await useKeyvAuthState('bot1', 'mysql://user:pass@localhost/db')
const { state, saveCreds } = await useKeyvAuthState('bot1', 'sqlite://auth.db')
const { state, saveCreds } = await useKeyvAuthState('bot1', 'etcd://localhost:2379')
const { state, saveCreds } = await useKeyvAuthState('bot1', 'memcache://localhost:11211')
const { state, saveCreds } = await useKeyvAuthState('test')
Supported connection string protocols: redis:/rediss:, mongodb:/mongodb+srv:, postgres:/postgresql:, mysql:, sqlite:, etcd:, memcache:/memcached:. All matching driver packages (@keyv/redis, @keyv/mongo, @keyv/postgres, @keyv/mysql, @keyv/sqlite, @keyv/etcd, @keyv/memcache) ship as regular dependencies of this package — nothing extra to install.
For backends without a connection string (e.g. DynamoDB), or to reuse one connection across multiple useKeyvAuthState calls yourself, pass a pre-built adapter or Keyv instance instead of a string:
import { useKeyvAuthState } from '@zervonex/baileys'
import KeyvMongo from '@keyv/mongo'
const backend = new KeyvMongo('mongodb://localhost/baileys')
const { state, saveCreds } = await useKeyvAuthState('bot1', backend)
Same connection string passed as a plain string is automatically cached and reused across every useKeyvAuthState call in the process — you don't need to build the instance yourself just to avoid opening duplicate connections, but you can if you want the reference for other purposes.
By default, Mongo/Postgres/MySQL/SQLite store everything in a collection/table named keyv (each adapter's own default). Set collectionName to use your own name instead — it's mapped to whatever each backend actually calls it (collection for Mongo, table for Postgres/MySQL/SQLite):
const { state, saveCreds } = await useKeyvAuthState('bot1', 'mongodb://localhost/baileys', {
collectionName: 'baileys_auth',
})
Per-type backend routing (e.g. prekeys on SQLite, sessions on Postgres) is not currently supported — one useKeyvAuthState call uses one backend for every key type.
Clearing a session (logout)
const { state, saveCreds, clearSession } = await useKeyvAuthState('bot1', 'redis://localhost:6379')
await clearSession()
clearSession() wipes every key belonging to that sessionId — creds, prekeys, sessions, sender keys, everything — by iterating the store filtering by the sessionId:: prefix and calling deleteMany. Only this session's keys are removed; other sessions sharing the same backend are untouched. There is no equivalent method on useMultiFileAuthState — delete the session folder instead.
Checking auth without loading full state
import { hasKeyvAuth } from '@zervonex/baileys'
const valid = await hasKeyvAuth('bot1', backend)
hasKeyvAuth() queries the backend using the correct key prefix — the same way useKeyvAuthState does internally — so it always returns the right answer regardless of your backend. Useful for checking session ownership before deciding whether to restore a connection, without the overhead of initializing a full auth state.
Options
preKeyRetention | number | 150 | Number of recent prekeys to keep |
cleanupThreshold | number | 50 | New prekeys generated before cleanup runs |
logger | object | undefined | Logger with .info / .warn methods |
collectionName | string | adapter's own default ("keyv") | Custom collection/table name — Keyv-based adapter only, ignored for connection-string-less (in-memory) and pre-built adapter/Keyv instances |
Return values
state | ✅ | ✅ |
saveCreds | ✅ | ✅ |
getStats | ✅ | ❌ |
clearSession | ❌ (delete the folder instead) | ✅ |
close | ❌ | ✅ (drops local listeners; safe to call, does not disconnect a shared backend) |
💬 Sending Messages
sock.sendMessage(jid, content, options?) handles every message type. The jid can be a user, group, status broadcast, or newsletter.
'1234567890@s.whatsapp.net'
'123456789-987654321@g.us'
'status@broadcast'
'120363...@newsletter'
Content Key Reference
Every key you can pass to sock.sendMessage:
text | Plain text or markdown |
image | Image message |
video | Video message |
audio | Audio / voice message |
document | File / document |
sticker | Sticker |
location | Location pin |
contacts | Contact card(s) |
react | Emoji reaction |
poll | Poll creation |
forward | Forward a message |
delete | Delete a message |
edit | Edit a sent message |
pin | Pin / unpin a message |
keep | Keep / unkeep a message |
disappearingMessagesInChat | Set disappearing timer |
aiRich | AI Rich message (text, code, table, images, sources, reels, latex, parts) |
albumMessage | Media album (2+ images/videos) |
carouselMessage | Interactive card carousel |
groupStatus | Post text/media to a group's story |
interactiveMessage | Raw interactive message object |
requestPaymentMessage | Payment request |
orderMessage | Order message |
eventMessage | Event / calendar invite |
stickerPack | Sticker pack |
verticalAlbumMessage | Vertical stacked media album (AIRich format) |
gridImageMessage | Image grid (GRID_IMAGE protocol type) |
videoGridMessage | Video grid (GenAIVideoPrimitive grid) |
a2uiMessage | A2UI bloksWidget interactive message |
statusMentionMessage | Mention someone in a status |
pollResultMessage | Poll result snapshot |
deliveryMode | Controls which devices receive the message — see Delivery Mode section |
ai | Mark message as AI-generated (injects bot label + supportPayload) |
Text Messages
await sock.sendMessage(jid, { text: 'Hello World!' })
await sock.sendMessage(jid, { text: 'Check this out: https://github.com' })
await sock.sendMessage(jid, { text: 'This is a reply' }, { quoted: message })
await sock.sendMessage(jid, {
text: '@1234567890 hey!',
mentions: ['1234567890@s.whatsapp.net']
})
await sock.sendMessage(jid, { text: 'AI response here', ai: true })
await sock.sendMessage(jid, { text: 'AI response here' }, { ai: true })
await sock.sendMessage(jid, { text: 'AI response here' }, { aiLabel: true })
const sock = makeWASocket({ aiLabel: true })
Delivery Mode
Control which devices receive your message using the deliveryMode option. By default every device gets the message (toAll).
toAll | ✅ | ✅ | ✅ | ✅ |
toMainOnly | ✅ | ❌ | ✅ | ❌ |
toRecipientMain | ✅ | ✅ | ✅ | ❌ |
toSelf | ✅ | ✅ | ❌ | ❌ |
toRecipient | ❌ | ❌ | ✅ | ✅ |
toSelfMain | ✅ | ❌ | ❌ | ❌ |
toRecipientMainOnly | ❌ | ❌ | ✅ | ❌ |
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toAll' })
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toMainOnly' })
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toRecipientMain' })
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toSelf' })
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toRecipient' })
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toSelfMain' })
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toRecipientMainOnly' })
deliveryMode is optional — omitting it defaults to toAll and behaves exactly like normal.
Media Messages
You can pass { url: '...' }, a Buffer, a file path string, or { stream: Stream } for any media type.
Image
await sock.sendMessage(jid, {
image: { url: 'https://example.com/image.jpg' },
caption: 'Caption here'
})
await sock.sendMessage(jid, { image: buffer, caption: 'From buffer' })
await sock.sendMessage(jid, { image: fs.readFileSync('./image.jpg') })
await sock.sendMessage(jid, { image: { url: './secret.jpg' }, viewOnce: true })
Video
await sock.sendMessage(jid, {
video: { url: './video.mp4' },
caption: 'Watch this!',
gifPlayback: false,
ptv: false
})
Audio
await sock.sendMessage(jid, {
audio: { url: './audio.mp3' },
mimetype: 'audio/mp4',
ptt: true
})
Convert to voice-compatible format: ffmpeg -i input.mp4 -avoid_negative_ts make_zero -ac 1 output.ogg
Document
await sock.sendMessage(jid, {
document: { url: './file.pdf' },
fileName: 'document.pdf',
mimetype: 'application/pdf',
caption: 'Here is the file'
})
Sticker
await sock.sendMessage(jid, { sticker: { url: './sticker.webp' } })
Location
await sock.sendMessage(jid, {
location: {
degreesLatitude: 24.121231,
degreesLongitude: 55.1121221,
name: 'Location Name',
address: 'Full Address Here'
}
})
Contact
const vcard = 'BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nORG:zervonex;\nTEL;type=CELL;waid=1234567890:+1 234 567 890\nEND:VCARD'
await sock.sendMessage(jid, {
contacts: {
displayName: 'John Doe',
contacts: [{ vcard }]
}
})
Reaction
await sock.sendMessage(jid, {
react: {
text: '💖',
key: message.key
}
})
Poll
await sock.sendMessage(jid, {
poll: {
name: 'Favorite Color?',
values: ['Red', 'Blue', 'Green'],
selectableOptionsCount: 1,
toAnnouncementGroup: false
}
})
await sock.sendMessage(jid, {
poll: {
name: 'What is 2+2?',
values: ['3', '4', '5'],
selectableOptionsCount: 1,
pollType: 1,
correctAnswer: '4'
}
})
await sock.sendMessage(jid, {
poll: {
name: 'Quick vote!',
values: ['Yes', 'No'],
endTime: Math.floor(Date.now() / 1000) + 3600,
hideParticipantName: true,
allowAddOption: false
}
})
await sock.poll()
.name('Favorite Color?')
.options(['Red', 'Blue', 'Green'])
.send(jid)
Pin Message
await sock.sendMessage(jid, {
pin: {
type: 1,
time: 86400,
key: message.key
}
})
Keep Message
await sock.sendMessage(jid, {
keep: message.key,
type: 1,
time: 86400
})
Disappearing Messages
await sock.sendMessage(jid, { disappearingMessagesInChat: 86400 })
await sock.sendMessage(jid, { disappearingMessagesInChat: false })
Forward Message
await sock.sendMessage(jid, { forward: message, force: true })
Edit Message
await sock.sendMessage(jid, {
text: 'Updated text',
edit: originalMessage.key
})
Delete Message
const sent = await sock.sendMessage(jid, { text: 'hello' })
await sock.sendMessage(jid, { delete: sent.key })
🎭 Fake Quoted Messages
Fake a reply without an actual original message — useful for AI responses, status reposts, custom UIs and more. Only the bare minimum fields are needed — WhatsApp fills in the rest visually.
Text Quote
await sock.sendMessage(jid, { text: 'Actual message' }, {
quoted: {
key: { remoteJid: jid, fromMe: false, id: 'FAKE_' + Date.now(), participant: '1234567890@s.whatsapp.net' },
message: { conversation: 'Fake quoted text' }
}
})
Image Quote
await sock.sendMessage(jid, { text: 'Replying to image' }, {
quoted: {
key: { remoteJid: jid, fromMe: false, id: 'FAKE_' + Date.now(), participant: '1234567890@s.whatsapp.net' },
message: { imageMessage: { caption: 'Fake image caption' } }
}
})
Sticker Quote
await sock.sendMessage(jid, { text: 'Replying to sticker' }, {
quoted: {
key: { remoteJid: jid, fromMe: false, id: 'FAKE_' + Date.now(), participant: '1234567890@s.whatsapp.net' },
message: { stickerMessage: {} }
}
})
AI / Bot Quote
await sock.sendMessage(jid, { text: 'My response' }, {
quoted: {
key: { remoteJid: jid, fromMe: true, id: 'FAKE_' + Date.now() },
message: { extendedTextMessage: { text: 'AI generated this', title: 'NexusAI' } }
}
})
Status Quote
await sock.sendMessage(jid, { text: 'Replying to your status!' }, {
quoted: {
key: { remoteJid: 'status@broadcast', fromMe: false, id: 'FAKE_' + Date.now(), participant: '1234567890@s.whatsapp.net' },
message: { conversation: 'Original status text' }
}
})
Key Fields
remoteJid | Where the quoted message appears to be from (jid, status@broadcast, group etc.) |
fromMe | true = appears sent by you/bot, false = appears sent by someone else |
id | Any unique string — 'FAKE_' + Date.now() avoids collisions |
participant | Required in groups/status — the person who appears to have sent it |
Buttons & Interactive Messages
Text with Buttons
await sock.sendMessage(jid, {
text: 'Choose an option',
footer: '© zervonex',
buttons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({ display_text: 'Option 1', id: 'opt1' })
},
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({
display_text: 'Visit Website',
url: 'https://zervonex.com',
merchant_url: 'https://zervonex.com'
})
}
]
})
Image / Video / Document with Buttons
await sock.sendMessage(jid, {
image: { url: 'https://example.com/image.jpg' },
caption: 'Description',
footer: '© zervonex',
buttons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({ display_text: 'Reply', id: 'btn1' })
}
]
})
All Button Types
| Quick Reply | quick_reply | display_text, id |
| URL | cta_url | display_text, url, merchant_url |
| Call | cta_call | display_text, phone_number |
| Copy | cta_copy | display_text, copy_code |
| Open Webview | cta_open_webview | display_text, url |
| Reminder | cta_reminder | display_text |
| Cancel Reminder | cta_cancel_reminder | display_text |
| Address | address | display_text |
| Send Location | send_location | display_text |
| Flow | flow | display_text, flow_id, flow_token |
| Catalog | catalog | display_text |
| Review & Pay | review_and_pay | display_text |
| Review Order | review_order | display_text |
| Order Details | order_details | display_text |
| Payment Status | payment_status | display_text |
| Transaction Details | transaction_details | display_text |
| List/Select | single_select | title, sections[].rows[] |
List / Single Select
{
name: 'single_select',
buttonParamsJson: JSON.stringify({
title: 'Select Option',
sections: [{
title: 'Section 1',
highlight_label: 'Popular',
rows: [
{ title: 'Option 1', description: 'Desc 1', id: 'opt1' },
{ title: 'Option 2', description: 'Desc 2', id: 'opt2' }
]
}]
})
}
Full Interactive Message
await sock.sendMessage(jid, {
interactiveMessage: {
title: 'Interactive',
footer: '© zervonex',
image: { url: 'https://example.com/image.jpg' },
nativeFlowMessage: {
messageParamsJson: JSON.stringify({ }),
buttons: [
{
name: 'single_select',
buttonParamsJson: JSON.stringify({
title: 'Select',
sections: [{ title: 'Options', rows: [{ title: 'Option 1', id: 'opt1' }] }]
})
},
{
name: 'cta_copy',
buttonParamsJson: JSON.stringify({ display_text: 'Copy Code', copy_code: 'NEXUS2025' })
}
]
}
}
})
interactiveButtons Shorthand
await sock.sendMessage(jid, {
text: 'Description of Message',
title: 'Title of Message',
subtitle: 'Subtitle Message',
footer: 'Footer Message',
interactiveButtons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({ display_text: 'Quick Reply', id: 'button_1' })
},
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({ display_text: 'Visit Website', url: 'https://zervonex.com' })
}
]
}, { quoted: message })
await sock.sendMessage(jid, {
image: { url: 'https://example.jpg' },
caption: 'Description of Message',
title: 'Title of Message',
subtitle: 'Subtitle Message',
footer: 'Footer Message',
media: true,
interactiveButtons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({ display_text: 'Quick Reply', id: 'button_1' })
},
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({ display_text: 'Visit Website', url: 'https://zervonex.com' })
}
]
}, { quoted: message })
await sock.sendMessage(jid, {
product: {
productImage: { url: 'https://example.jpg' },
productImageCount: 1,
title: 'Product Title',
description: 'Product Description',
priceAmount1000: 20000 * 1000,
currencyCode: 'USD',
retailerId: 'Retail',
url: 'https://example.com'
},
businessOwnerJid: '1234@s.whatsapp.net',
caption: 'Description of Message',
title: 'Title of Message',
footer: 'Footer Message',
media: true,
interactiveButtons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({ display_text: 'Quick Reply', id: 'button_1' })
},
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({ display_text: 'Visit Website', url: 'https://zervonex.com' })
}
]
}, { quoted: message })
Classic Buttons (Old Style)
For compatibility with older WhatsApp versions or specific use cases:
await sock.sendMessage(jid, {
text: 'Message with classic buttons',
footer: 'Footer text',
buttons: [
{ buttonId: 'btn1', buttonText: { displayText: 'Button 1' }, type: 1 },
{ buttonId: 'btn2', buttonText: { displayText: 'Button 2' }, type: 1 }
],
headerType: 1
})
await sock.sendMessage(jid, {
image: { url: 'https://example.com/image.jpg' },
caption: 'Body text',
footer: 'Footer',
buttons: [
{ buttonId: 'btn1', buttonText: { displayText: 'Button 1' }, type: 1 }
],
headerType: 3
})
Header Types:
0 | EMPTY | No header |
1 | TEXT | Text header |
2 | DOCUMENT | Document header |
3 | IMAGE | Image header |
4 | VIDEO | Video header |
5 | LOCATION | Location header |
Button Types:
1 | RESPONSE | Standard clickable button |
2 | NATIVE_FLOW | Native flow button |
Extended Message Types
All of these go through sock.sendMessage — the library detects the type automatically.
Album
await sock.sendMessage(jid, {
albumMessage: [
{ image: { url: 'https://example.com/1.jpg' }, caption: 'Photo 1' },
{ image: { url: 'https://example.com/2.jpg' }, caption: 'Photo 2' },
{ video: { url: 'https://example.com/3.mp4' }, caption: 'Video 1' }
]
}, { albumDelay: 1500 })
await sock.sendAlbumMessage(jid, [
{ image: { url: 'https://example.com/1.jpg' }, caption: 'Photo 1' },
{ video: { url: 'https://example.com/2.mp4' }, caption: 'Video 1' }
], quoted)
await sock.album()
.image('https://example.com/1.jpg', 'Photo 1')
.image('https://example.com/2.jpg', 'Photo 2')
.video('https://example.com/3.mp4', 'Video 1')
.send(jid)
Carousel
await sock.sendMessage(jid, {
carouselMessage: {
caption: 'Our Products',
footer: 'Powered by zervonex',
cards: [
{
header: { title: 'Card 1', hasMediaAttachment: false },
body: { text: 'Description of card 1' },
footer: { text: 'Footer' },
nativeFlowMessage: {
buttons: [
{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Select', id: 'card1' }) },
{ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Learn More', url: 'https://zervonex.com' }) }
]
}
},
{
header: { title: 'Card 2', hasMediaAttachment: false },
body: { text: 'Description of card 2' },
footer: { text: 'Footer' },
nativeFlowMessage: {
buttons: [
{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Select', id: 'card2' }) }
]
}
}
]
}
})
await sock.carousel()
.setCaption('Our Products')
.setFooter('Powered by zervonex')
.card(sock.button().setBody('Card 1 description').reply('Select', 'card1').url('Learn More', 'https://zervonex.com'))
.card(sock.button().setBody('Card 2 description').reply('Select', 'card2'))
.send(jid)
Event
await sock.sendMessage(jid, {
eventMessage: {
name: 'NexusTech Conference',
description: 'Annual developer conference',
location: { degreesLatitude: 6.5244, degreesLongitude: 3.3792, name: 'Lagos, Nigeria' },
joinLink: 'https://meet.example.com/nexusconf',
startTime: Math.floor(Date.now() / 1000) + 86400,
endTime: Math.floor(Date.now() / 1000) + 172800,
extraGuestsAllowed: true,
hasReminder: true,
reminderOffsetSec: 3600
}
})
await sock.event()
.setName('NexusTech Conference')
.setDescription('Annual developer conference')
.setLocation({ degreesLatitude: 6.5244, degreesLongitude: 3.3792, name: 'Lagos, Nigeria' })
.setJoinLink('https://meet.example.com/nexusconf')
.setStartTime(Math.floor(Date.now() / 1000) + 86400)
.setEndTime(Math.floor(Date.now() / 1000) + 172800)
.setReminder(3600)
.send(jid)
Request Payment
await sock.sendMessage(jid, {
requestPaymentMessage: {
currency: 'NGN',
amount: 10000000,
from: '1234567890@s.whatsapp.net',
note: 'Payment for order #123'
}
})
await sock.payment()
.setCurrency('NGN')
.setAmount(10000000)
.setFrom('1234567890@s.whatsapp.net')
.setNote('Payment for order #123')
.send(jid)
Order
await sock.sendMessage(jid, {
orderMessage: {
orderId: 'ORDER_001',
itemCount: 3,
totalAmount1000: 45000000,
totalCurrencyCode: 'NGN',
orderTitle: 'My Order',
message: 'Thank you for your order',
sellerJid: sock.user.id,
thumbnail: { url: 'https://example.com/product.jpg' }
}
})
await sock.order()
.from({ orderId: 'ORDER_001', itemCount: 3, totalAmount1000: 45000000, totalCurrencyCode: 'NGN', orderTitle: 'My Order' })
.send(jid)
Product Message
await sock.sendMessage(jid, {
productMessage: {
title: 'Product Title',
description: 'Product Description',
thumbnail: 'https://example.com/img.png',
productId: '123456789',
retailerId: 'STORE',
url: 'https://example.com',
body: 'Product Body',
footer: 'Product Footer',
buttons: [
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({ display_text: 'Buy Now', url: 'https://example.com' })
}
]
}
}, { quoted: message })
Poll Result (Newsletter Style)
await sock.sendMessage(jid, {
pollResult: {
name: 'Poll Title',
votes: [['Option A', 42], ['Option B', 17]]
}
}, { quoted: message })
Group Story
Post a story/status visible only to a specific group.
All media types accept a URL, Buffer, file path, or WhatsApp CDN URL — no manual download needed.
await sock.sendMessage(jid, { groupStatus: { text: 'Hello group!' } })
await sock.sendMessage(jid, { groupStatus: { image: { url: 'https://example.com/image.jpg' }, caption: 'Caption' } })
await sock.sendMessage(jid, { groupStatus: { video: { url: 'https://example.com/video.mp4' }, caption: 'Caption' } })
await sock.sendMessage(jid, { groupStatus: { audio: { url: 'https://example.com/audio.mp3' }, ptt: false } })
await sock.sendGroupStatusMessage(groupJid, { text: 'Hello group!' })
await sock.sendGroupStatusMessage(groupJid, { image: { url: 'https://example.com/image.jpg' }, caption: 'Caption' })
await sock.sendGroupStatusMessage(groupJid, { video: { url: 'https://example.com/video.mp4' }, caption: 'Caption' })
await sock.sendGroupStatusMessage(groupJid, { audio: { url: 'https://example.com/audio.mp3' }, ptt: false })
await sock.sendGroupStatusMessage(groupJid, { image: buffer })
await sock.sendGroupStatusMessage(groupJid, { image: fs.readFileSync('./image.jpg') })
Personal Status / Story (status@broadcast)
Post a story visible to all your contacts or a specific list.
await sock.sendMessage('status@broadcast', { text: 'My status update!' })
await sock.sendMessage('status@broadcast', { image: { url: 'https://example.com/image.jpg' }, caption: 'Caption' })
await sock.sendMessage('status@broadcast', { video: { url: 'https://example.com/video.mp4' }, caption: 'Caption' })
await sock.sendMessage('status@broadcast', { audio: { url: 'https://example.com/audio.mp3' }, ptt: false })
await sock.sendMessage('status@broadcast', {
text: 'Private status',
statusJidList: [
'1234567890@s.whatsapp.net',
'0987654321@s.whatsapp.net'
]
})
React to a Status
await sock.sendMessage('status@broadcast', {
react: {
text: '❤️',
key: message.key
}
}, {
statusJidList: [message.key.participant]
})
await sock.sendReaction('status@broadcast', message.key, '❤️', {
statusJidList: [message.key.participant]
})
Status Mention (tag someone in your story)
Mention specific users or groups in a story. They get a private notification.
await sock.sendStatusMentions({ text: 'Hey @someone check this out!' }, ['1234567890@s.whatsapp.net'])
await sock.sendStatusMentions({ image: { url: 'https://example.com/image.jpg' }, caption: 'Caption' }, ['1234567890@s.whatsapp.net'])
await sock.sendStatusMentions({ video: { url: 'https://example.com/video.mp4' } }, ['1234567890@s.whatsapp.net'])
await sock.sendStatusMentions({ audio: { url: 'https://example.com/audio.mp3' }, ptt: true }, ['1234567890@s.whatsapp.net'])
await sock.sendStatusMentions({ text: 'Hey group!' }, ['123456789@g.us'])
Sticker Pack
Send a full sticker pack. Supports buffers, file paths, and URLs. Packs over 60 stickers are automatically batched with a 2-second delay between batches.
await sock.sendMessage(jid, {
stickerPack: {
name: 'My Stickers',
publisher: 'zervonex',
stickers: [
{ data: buffer, emojis: ['😊'] },
{ data: './sticker.png', emojis: ['😂'] },
{ data: './sticker.webp', emojis: ['🎉'] },
{ data: './sticker.gif', emojis: ['✨'], isAnimated: true },
{ data: './sticker.webm', emojis: ['🎬'], isAnimated: true },
{ data: 'https://example.com/sticker.jpg', emojis: ['❤️'] },
{ data: buffer, type: StickerTypes.ROUNDED, emojis: ['🔥'] },
],
cover: coverBuffer
}
}, { quoted: message })
await sock.stickerPackMessage(jid, {
name: 'My Stickers',
publisher: 'zervonex',
stickers: [
{ data: buffer, emojis: ['😊'] },
{ data: './sticker.png', emojis: ['😂'] }
],
cover: coverBuffer
}, { quoted: message })
Premium Stickers
Pass premium on a single sticker:
await sock.sendMessage(jid, { sticker: buffer, premium: 1 })
Per-sticker in a pack:
await sock.sendMessage(jid, { stickerPack: { stickers: [{ data: buffer, emojis: ['🔥'], premium: 1 }] } })
Whole pack as premium:
await sock.sendMessage(jid, { stickerPack: { premium: 1, stickers: [...] } })
Per-sticker options:
data | Buffer | string | required | Buffer, file path, URL, or SVG string |
emojis | string[] | [] | Associated emojis shown in WhatsApp sticker picker |
type | StickerTypes | ROUNDED | Crop/shape style (see below) |
isAnimated | boolean | false | Mark as animated (for GIF/WebM inputs) |
isLottie | boolean | false | Mark as Lottie animated (for TGS inputs) |
accessibilityLabel | string | '' | Accessibility label for the sticker |
Sticker types (StickerTypes):
StickerTypes.DEFAULT | Original aspect ratio, no crop |
StickerTypes.CROPPED | Cropped to 512×512 |
StickerTypes.FULL | Stretched to fill 512×512 |
StickerTypes.CIRCLE | Circular mask |
StickerTypes.ROUNDED | Rounded corners |
Supported input formats:
| WebP | ✅ Full | Static and animated, passed through sharp |
| PNG | ✅ Full | Auto-converted to WebP |
| JPG/JPEG | ✅ Full | Auto-converted to WebP |
| BMP | ✅ Full | Auto-converted to WebP |
| SVG | ✅ Full | Rendered and converted to WebP |
| GIF | ✅ Animated | Converted to animated WebP via sharp |
| WebM (VP9) | ✅ Animated | Converted via ffmpeg → animated WebP |
| MP4 | ✅ Animated | Converted via ffmpeg → animated WebP |
| TGS | ✅ Animated | Telegram gzip-compressed Lottie — converted via rlottie WASM → animated WebP |
Key behaviours:
- Packs >60 stickers are auto-batched with a 2-second delay between batches
- Animated stickers (WebM/MP4) are converted directly via ffmpeg for best quality; static formats go through wa-sticker-formatter. EXIF metadata is embedded automatically for all formats.
- Invalid or unprocessable stickers are gracefully skipped
- Cover defaults to the first sticker if not provided
AI Rich Messages
All types can be sent two ways:
sock.sendMessage with an aiRich content key
sock.airich() fluent builder — chain methods and call .send(jid)
Code Block
await sock.sendMessage(jid, {
aiRich: {
language: 'javascript',
code: `async function fetchUser(id) {
const res = await fetch(\`/api/users/\${id}\`)
if (!res.ok) throw new Error('Not found')
return res.json()
}`,
header: '**Fetch User Example**',
footer: '_Requires Node 18+_'
}
}, { quoted: message })
await sock.airich()
.setTitle('Code Example')
.addCode('javascript', `const greet = name => \`Hello, \${name}!\``)
.send(jid)
await sock.sendCodeBlock(jid, code, quoted, { language: 'javascript', title: 'Example', footer: '© zervonex' })
await sock.sendCodeBlockV2(jid, code, quoted, { language: 'go', title: 'Go Example', text: 'Here is a Go snippet:', footer: '© zervonex' })
Supported languages: Any of the 190+ languages supported by PrismJS — javascript, typescript, python, rust, go, java, cpp, kotlin, swift, bash, sql, dockerfile, solidity, graphql, and many more.
Table
await sock.sendMessage(jid, {
aiRich: {
table: [
'Java vs JavaScript',
['Feature', 'Java', 'JavaScript'],
['Type', 'Compiled', 'Interpreted'],
['Typing', 'Static', 'Dynamic'],
['Main Use', 'Enterprise', 'Web']
],
footer: 'Hope this helps!'
}
}, { quoted: message })
await sock.airich()
.setTitle('Comparison')
.addTable([
['Feature', 'Java', 'JavaScript'],
['Type', 'Compiled', 'Interpreted'],
['Typing', 'Static', 'Dynamic']
])
.send(jid)
await sock.sendTable(jid, 'Java vs JavaScript', ['Feature', 'Java', 'JavaScript'], [
['Type', 'Compiled', 'Interpreted'],
['Typing', 'Static', 'Dynamic']
], quoted, { footer: 'Hope this helps!' })
await sock.sendTableV2(jid, [
'Java vs JavaScript',
'Feature | Java | JavaScript',
'Type | Compiled | Interpreted;;Typing | Static | Dynamic'
], quoted, { footer: 'Hope this helps!' })
await sock.sendList(jid, 'Bot Info', [
['Name', 'zervonex'],
['Version', '3.0.2'],
['Developer', 'zervonex']
], quoted, { footer: '© zervonex' })
Text
await sock.sendMessage(jid, {
aiRich: {
text: '## Hello\n\nThis supports **markdown**:\n\n- Bold\n- Lists\n- Headers',
footer: '_Powered by zervonex_'
}
}, { quoted: message })
await sock.sendMessage(jid, {
aiRich: {
texts: ['## Title', 'First paragraph', 'Second paragraph'],
footer: '_zervonex_'
}
}, { quoted: message })
await sock.airich()
.addText('## Hello World\nThis is *bold* and _italic_.')
.addFOAText('# zervonex\n## Powered by @zervonex/baileys')
.addTip('Always validate user input before processing.')
.addMetadata('Generated just now · zervonex v3.0.2')
.send(jid)
Links & Search Sources
await sock.sendMessage(jid, {
aiRich: {
text: 'Upload complete:\n✅ Freeimage — {{IE_0}}view here{{/IE_0}}\n✅ Yardsansh — {{IE_1}}view here{{/IE_1}}',
sources: [
{ url: 'https://freeimage.host', displayName: 'Freeimage', subtitle: 'freeimage.host' },
{ url: 'https://yardsansh.com', displayName: 'Yardsansh', subtitle: 'yardsansh.com' }
],
footer: '✨ Done!'
}
}, { quoted: message })
await sock.airich()
.addText('Top results:')
.addSource([
{ title: 'MDN Web Docs', url: 'https://developer.mozilla.org', favicon: 'https://developer.mozilla.org/favicon.ico' },
{ title: 'NPM Registry', url: 'https://npmjs.com', favicon: 'https://static.npmjs.com/favicon.ico' }
])
.send(jid)
await sock.sendLink(jid, 'Results:\n🔗 {{IE_0}}link one{{/IE_0}}', ['https://example.com/1'], quoted, { headerText: '📁 Results' })
await sock.sendLinkV2(jid, 'Search results:', [{ url: 'https://npmjs.com', displayName: 'NPM', subtitle: 'npmjs.com' }], quoted)
Images, Video, Reels
await sock.sendMessage(jid, {
aiRich: {
text: 'Here are some results:',
images: [
{ url: 'https://example.com/img1.jpg', sourceUrl: 'https://example.com' },
{ url: 'https://example.com/img2.jpg', sourceUrl: 'https://example.com' }
]
}
}, { quoted: message })
await sock.airich()
.setTitle('Image Preview')
.addImage('https://example.com/image.jpg')
.addVideo('https://example.com/clip.mp4')
.addReels([{
creator: 'Rick Astley',
avatar_url: 'https://example.com/avatar.jpg',
thumbnail_url: 'https://example.com/thumb.jpg',
reels_url: 'https://youtube.com/watch?v=dQw4w9WgXcQ'
}])
.send(jid)
await sock.airich()
.addText('What would you like to do?')
.addSuggest(['📥 Download Video', '🎨 Generate Image', '🔍 Web Search'])
.addWidget([
{ label: '📥 Download', tool_call_id: 'dl', kind: 'OTHER', toast: 'Downloading...' },
{ label: '✅ Confirm', tool_call_id: 'confirm', kind: 'CONFIRM', toast: 'Confirmed!' },
{ label: '❌ Cancel', tool_call_id: 'cancel', kind: 'CANCEL', toast: 'Cancelled.' }
])
.addFooterAction({ cta_text: '🔗 Visit GitHub', cta_type: 'OPEN_URL', cta_url: 'https://github.com/nexustechpro2' })
.send(jid)
Status Indicators
await sock.airich()
.addText('Processing your request...')
.addThinkingStatus('Searching the web...', 'WEB_SEARCH')
.addThinkingStatus('Thinking deeply...', 'THINKING')
.addProgressStatus('Almost done — 80%', true)
.addProgressStatus('Done!', false)
.send(jid)
await sock.airich()
.setTitle('Job Queue')
.addTask({ taskId: 'task-1', title: 'Fetch data', subtitle: 'Calling API', status: 'DONE' })
.addTask({ taskId: 'task-2', title: 'Process result', status: 'RUNNING' })
.addTask({ taskId: 'task-3', title: 'Send response', status: 'PENDING' })
.send(jid)
Dividers & Spacers
await sock.airich()
.setTitle('Layout Test')
.addText('*Section A*')
.addDivider()
.addText('*Section B*')
.addDivider('DOT')
.addText('*Section C*')
.addSpacer(3)
.addText('*Section D*')
.send(jid)
LaTeX
await sock.sendMessage(jid, {
aiRich: {
latexText: 'The quadratic formula:',
latex: [
{ latexExpression: 'x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}', url: 'https://example.com/formula.png', width: 400, height: 80 }
]
}
}, { quoted: message })
await sock.airich()
.addText("Einstein's famous equation:")
.addLatex({ expression: 'E = mc^2', imageUrl: 'https://example.com/emc2.png', width: 300, height: 60 })
.send(jid)
await sock.sendLatex(jid, [
{ latexExpression: 'E = mc^2', url: 'https://example.com/emc2.png', width: 300, height: 60 }
], quoted, { text: 'Mass-energy equivalence:' })
Note: LaTeX requires a pre-rendered image URL. WhatsApp does not render LaTeX client-side.
Post & Product Cards
await sock.airich()
.addPost({
title: 'zervonex v3.0.2 🚀',
username: 'nexustechpro2',
subtitle: 'GitHub',
thumbnail_url: 'https://example.com/thumb.jpg',
post_url: 'https://github.com/nexustechpro2',
post_caption: 'New release — faster, smarter, better.',
source_app: 'instagram',
likes_count: 2048,
comments_count: 128,
is_verified: true
})
.addProduct({
title: 'zervonex Premium',
brand: 'zervonex',
price: '$9.99/mo',
sale_price: '$4.99/mo',
product_url: 'https://github.com/nexustechpro2',
image_url: 'https://example.com/product.jpg'
})
.send(jid)
Map
await sock.airich()
.addText('Our location:')
.addMap([{
latitude: 6.5244,
longitude: 3.3792,
name: 'Lagos, Nigeria',
address: 'Lagos Island, Lagos State'
}])
.send(jid)
Generating Placeholder
await sock.airich()
.addText('Generating your image...')
.addImagine({ mediaType: 'image', status: 'GENERATING', estimatedMs: 5000 })
.send(jid)
const { msg, responseId, itemId } = await sock.sendRichGeneration(jid, {
text: 'Generating...',
mediaType: 'video',
status: 'GENERATING',
estimatedMs: 8000
})
await sock.airich()
.setResponseId(responseId)
.addText('Here is your video!')
.addImagine({ mediaType: 'video', url: 'https://example.com/result.mp4', status: 'READY' })
.sendEdit(jid, msg.key.id)
Load From / Edit
const r = sock.airich()
r.loadFrom(receivedMessage.message)
r.addText('Additional content added!')
await r.send(jid)
const sent = await sock.airich().addText('Original text').send(jid)
await sock.airich()
.addText('Updated text')
.sendEdit(jid, sent.key.id)
Capture & Relay
const captured = sock.captureAiRich(msg.message)
if (captured) await sock.relayAiRich(targetJid, captured)
Full Combo Example
await sock.sendMessage(jid, {
aiRich: {
parts: [
{ type: 'text', content: '## String Reversal' },
{ type: 'code', language: 'javascript', content: `const reverse = s => s.split('').reverse().join('')` },
{ type: 'text', content: 'Performance:' },
{ type: 'table', table: ['Benchmarks', ['Method', 'Ops/sec'], ['split.reverse.join', '1.2M'], ['manual loop', '2.1M']] },
{ type: 'sources', sources: [{ url: 'https://jsperf.app', displayName: 'JSPerf', subtitle: 'jsperf.app' }] }
],
footer: '_zervonex_'
}
}, { quoted: message })
await sock.airich()
.setTitle('zervonex Bot')
.setFooter('Powered by @zervonex/baileys latest')
.addText('*Welcome to zervonex!*')
.addDivider()
.addCode('python', 'print("Hello from zervonex")')
.addTable([
['Plan', 'Price', 'Sessions'],
['Basic', 'Free', '1'],
['Pro', '$9/mo', '10'],
['Enterprise', 'Custom', 'Unlimited']
])
.addTask({ taskId: 't1', title: 'Bot online', status: 'DONE' })
.addMetadata('zervonex • v3.0.2 • 2026')
.addSuggest(['View commands', 'Get support', 'Upgrade plan'])
.addFooterAction({ cta_text: '📖 View Dashboard', cta_type: 'OPEN_URL', cta_url: 'https://github.com/nexustechpro2' })
.addFOAText('_Reply with any message to continue_')
.send(jid)
await sock.sendRichMessage(jid, {
text: 'Hello from sendRichMessage!',
code: 'console.log("works!")',
language: 'javascript',
footer: '_zervonex_'
}, quoted)
aiRich Options Reference
text | string | Single markdown text block |
texts | string[] | Multiple text blocks in order |
code | string | { language, code } | Single code block |
codes | Array | Multiple code blocks |
language | string | Default language for code/codes |
table | Array | [title, headers[], ...rows[]] |
headers + rows | Array | Alternative table format |
images | Array | Grid images with url, sourceUrl |
sources | Array | Search source cards |
reels | Array | Horizontal reel cards |
latex | Array | LaTeX expression objects |
latexText | string | Text shown above latex |
parts | Array | Composite ordered parts array |
header | string | Text prepended before content |
footer | string | Text appended after content |
botJid | string | Custom bot JID |
responseId | string | Custom response UUID |
forwarded | boolean | Set false to disable forwarded context |
rawSections | Array | Raw unified response sections (advanced) |
rawSubmessages | Array | Matching submessages for rawSections |
Shorthand Wrappers
await sock.sendText(jid, 'Hello!', options)
await sock.sendImage(jid, { url: './image.jpg' }, 'Caption', options)
await sock.sendVideo(jid, buffer, 'Caption', options)
await sock.sendDocument(jid, { url: './file.pdf' }, 'Caption', options)
await sock.sendAudio(jid, { url: './audio.mp3' }, options)
await sock.sendLocation(jid, { degreesLatitude: 24.12, degreesLongitude: 55.11, name: 'Place' }, options)
await sock.sendPoll(jid, 'Question?', ['A', 'B', 'C'], false, options)
await sock.sendReaction(jid, message.key, '💖', options)
await sock.sendSticker(jid, { url: './sticker.webp' }, options)
await sock.sendContact(jid, { vcard }, options)
await sock.sendForward(jid, message, { force: true })
await sock.sendCodeBlock(jid, code, quoted, { language, title, footer })
await sock.sendCodeBlockV2(jid, code, quoted, { language, title, text, footer })
await sock.sendTable(jid, title, headers, rows, quoted, { headerText, footer })
await sock.sendTableV2(jid, tableArray, quoted, { headerText, text, footer })
await sock.sendList(jid, title, items, quoted, { footer })
await sock.sendLink(jid, text, links, quoted, { headerText, footer, botJid })
await sock.sendLinkV2(jid, text, links, quoted, { headerText, footer, searchEngine })
await sock.sendLatex(jid, expressions, quoted, { text, headerText, footer })
await sock.sendRichMessage(jid, data, quoted)
await sock.sendAlbumMessage(jid, items, quoted)
await sock.sendGroupStatusMessage(jid, content)
await sock.sendRichGeneration(jid, { text, mediaType, status, estimatedMs })
await sock.sendUnifiedResponse(jid, sections, submessages, options)
await sock.sendVerticalAlbumMessage(jid, { items: [...] })
await sock.sendGridImageMessage(jid, { imageUrls: [...] })
await sock.sendVideoGridMessage(jid, { videos: [...] })
await sock.sendA2UIMessage(jid, { bloksWidget, nativeFlowMessage })
await sock.sendA2UICommandMenu(jid, { title, rows, buttons, imageMessage })
✏️ Message Modifications
await sock.sendMessage(jid, { text: 'Updated text', edit: originalMessage.key })
const sent = await sock.sendMessage(jid, { text: 'hello' })
await sock.sendMessage(jid, { delete: sent.key })
await sock.sendMessage(jid, { pin: { type: 1, time: 86400, key: message.key } })
await sock.sendMessage(jid, { pin: { type: 0, time: 0, key: message.key } })
📥 Media Download
import { downloadMediaMessage, getContentType } from '@zervonex/baileys'
sock.ev.on('messages.upsert', async ({ messages }) => {
const msg = messages[0]
if (!msg.message) return
const type = getContentType(msg.message)
if (type === 'imageMessage') {
const buffer = await downloadMediaMessage(
msg,
'buffer',
{},
{ logger, reuploadRequest: sock.updateMediaMessage }
)
fs.writeFileSync('./downloaded.jpg', buffer)
}
})
await sock.updateMediaMessage(msg)
👥 Group Management
const group = await sock.groupCreate('Group Name', ['1234567890@s.whatsapp.net'])
await sock.groupParticipantsUpdate(groupJid, ['1234567890@s.whatsapp.net'], 'add')
await sock.groupParticipantsUpdate(groupJid, ['1234567890@s.whatsapp.net'], 'remove')
await sock.groupParticipantsUpdate(groupJid, ['1234567890@s.whatsapp.net'], 'promote')
await sock.groupParticipantsUpdate(groupJid, ['1234567890@s.whatsapp.net'], 'demote')
await sock.groupUpdateSubject(groupJid, 'New Group Name')
await sock.groupUpdateDescription(groupJid, 'New description')
await sock.groupSettingUpdate(groupJid, 'announcement')
await sock.groupSettingUpdate(groupJid, 'not_announcement')
await sock.groupSettingUpdate(groupJid, 'locked')
await sock.groupSettingUpdate(groupJid, 'unlocked')
const metadata = await sock.groupMetadata(groupJid)
const code = await sock.groupInviteCode(groupJid)
console.log(`https://chat.whatsapp.com/${code}`)
await sock.groupRevokeInvite(groupJid)
await sock.groupRevokeInviteV4(groupJid, '1234567890@s.whatsapp.net')
await sock.groupAcceptInvite('INVITE_CODE')
await sock.groupLeave(groupJid)
AI Groups
WhatsApp AI Groups are a special group type with Meta AI built-in as a participant.
const group = await sock.aiGroupCreate('AI Study Group', ['1234567890@s.whatsapp.net'])
const metadata = await sock.aiGroupMetadata(groupJid)
await sock.aiGroupAddBot(groupJid)
await sock.aiGroupParticipantsUpdate(groupJid, ['1234567890@s.whatsapp.net'], 'add')
await sock.aiGroupParticipantsUpdate(groupJid, ['1234567890@s.whatsapp.net'], 'remove')
await sock.aiGroupParticipantsUpdate(groupJid, ['1234567890@s.whatsapp.net'], 'promote')
await sock.aiGroupParticipantsUpdate(groupJid, ['1234567890@s.whatsapp.net'], 'demote')
await sock.aiGroupUpdateSubject(groupJid, 'New AI Group Name')
const code = await sock.aiGroupInviteCode(groupJid)
await sock.aiGroupRevokeInvite(groupJid)
await sock.aiGroupAcceptInvite('INVITE_CODE')
await sock.aiGroupLeave(groupJid)
await sock.aiGroupSettingUpdate(groupJid, 'announcement')
await sock.aiGroupToggleEphemeral(groupJid, 86400)
await sock.aiGroupToggleEphemeral(groupJid, 0)
📱 User Operations
const [result] = await sock.onWhatsApp('1234567890')
if (result?.exists) console.log(result.jid)
const url = await sock.profilePictureUrl(jid, 'image')
await sock.updateProfilePicture(jid, { url: './profile.jpg' })
await sock.removeProfilePicture(jid)
const status = await sock.fetchStatus(jid)
await sock.updateProfileStatus('Available 24/7')
await sock.updateProfileName('My Bot')
const biz = await sock.getBusinessProfile(jid)
await sock.presenceSubscribe(jid)
await sock.sendPresenceUpdate('composing', jid)
await sock.readMessages([message.key])
await sock.updateBlockStatus(jid, 'block')
await sock.updateBlockStatus(jid, 'unblock')
const blocked = await sock.fetchBlocklist()
Usernames
const result = await sock.checkUsername('zervonex')
const results = await sock.checkUsernameMulti(['zervonex', 'zervonex'])
await sock.setUsername('zervonex')
await sock.setUsername('zervonex', { pin: '1234' })
await sock.checkAndSetUsername('zervonex')
const username = await sock.getMyUsername()
await sock.deleteUsername()
await sock.setUsernamePin('1234')
const user = await sock.findUserByUsername('zervonex')
const user = await sock.findUserByUsername('zervonex', '1234')
const list = await sock.fetchContactUsernames('1234567890@s.whatsapp.net', '0987654321@s.whatsapp.net')
const suggestions = await sock.getUsernameRecommendations()
💬 Check Number Status
const result = await sock.checkStatusWA('1234567890')
import { checkStatusWA } from '@zervonex/baileys'
const result = await checkStatusWA('1234567890')
{ number: '+1234567890', status: 'active', isBanned: false, isNeedOfficialWa: false, banInfo: null }
{
number: '+1234567890',
status: 'banned',
isBanned: true,
isNeedOfficialWa: false,
banInfo: {
banType: 'temporary',
violationType: '2',
violationReason: 'Type 2',
canAppeal: true,
appealToken: 'xxxxx',
banTime: 1716840000,
banDate: '2024-05-28T00:00:00.000Z',
appealStatus: 'PENDING',
appealCreatedAt: '2024-05-27T00:00:00.000Z'
}
}
{ number: '+1234567890', status: 'blocked', isBanned: false, isNeedOfficialWa: true, banInfo: null }
{ number: '+1234567890', status: 'rate_limited', isBanned: false, isNeedOfficialWa: false, banInfo: null }
import { checkStatusWA } from '@zervonex/baileys'
const check = await checkStatusWA('1234567890')
if (check.isBanned) {
console.log(`Banned: ${check.banInfo.banType}`)
if (check.banInfo.canAppeal) console.log(`Appeal token: ${check.banInfo.appealToken}`)
} else {
console.log(`Status: ${check.status}`)
}
🔒 Privacy Controls
const settings = await sock.fetchPrivacySettings()
await sock.updateLastSeenPrivacy('contacts')
await sock.updateOnlinePrivacy('all')
await sock.updateProfilePicturePrivacy('contacts')
await sock.updateStatusPrivacy('contacts')
await sock.updateReadReceiptsPrivacy('all')
await sock.updateGroupsAddPrivacy('contacts')
Reporting
await sock.reportSpam(jid)
await sock.reportSpam(jid, {
spamFlow: 'ACCOUNT_INFO_REPORT',
maxMessages: 5,
messages: [...]
})
await sock.reportContact(jid)
await sock.reportContact(jid, [message.key, message2.key])
await sock.reportGroup(groupJid)
await sock.reportGroup(groupJid, [message.key, message2.key])
reportSpam | Contacts & Groups | ❌ | ❌ | ✅ |
reportContact | Contacts only | ✅ | ❌ | ❌ |
reportGroup | Groups only | ❌ | ✅ | ❌ |
reportSpam requires at least one message to have been received from the JID first — it pulls stored reporting metadata automatically.
Reporting Info Store
const info = sock.getStoredReportingInfo(jid)
const limited = sock.getStoredReportingInfo(jid, 3)
sock.clearStoredReportingInfo(jid)
console.log(sock.SPAM_FLOWS)
Extended Privacy (MEX-based)
await sock.updateTextStatus('Available for chats', '👋')
const statuses = await sock.getTextStatusList(['1234567890@s.whatsapp.net'])
const info = await sock.fetchUserPictureInfo('1234567890@s.whatsapp.net')
await sock.setProfilePictureMex(imageBase64)
const devices = await sock.getTrustedDevices()
await sock.addTrustedDevice('device_id', 'My Laptop')
await sock.untrustTrustedDevice('device_id')
await sock.deleteTrustedDevice('device_id')
await sock.addMultiAccountLink('1234567890')
await sock.revokeMultiAccount('1234567890@s.whatsapp.net')
const contacts = await sock.contactIntegrityQuery(['1234567890@s.whatsapp.net'])
const businesses = await sock.bizIntegrityQuery(['1234567890@s.whatsapp.net'])
await sock.linkedProfilesSet([{ type: 'FB', username: 'myprofile' }])
await sock.linkedProfilesRemove(['FB'])
await sock.linkedProfilesUpdate([{ type: 'FB', showOnProfile: true }])
await sock.migrateBlocklistLid(['1234567890@s.whatsapp.net'])
await sock.notifyPushName(groupJid, [{ jid: '1234567890@s.whatsapp.net', pushName: 'John' }])
💬 Chat Operations
const lastMsg = await getLastMessageInChat(jid)
await sock.chatModify({ archive: true, lastMessages: [lastMsg] }, jid)
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
await sock.chatModify({ mute: null }, jid)
await sock.chatModify({ pin: true }, jid)
await sock.chatModify({ pin: false }, jid)
await sock.chatModify({ delete: true, lastMessages: [{ key: lastMsg.key, messageTimestamp: lastMsg.messageTimestamp }] }, jid)
await sock.chatModify({ markRead: true }, jid)
await sock.chatModify({ markRead: false }, jid)
📢 Newsletter / Channels
const channel = await sock.newsletterCreate('Channel Name', 'Channel description')
console.log(channel.id)
const meta = await sock.newsletterMetadata('invite', 'INVITE_CODE')
const meta2 = await sock.newsletterMetadata('jid', newsletterJid)
await sock.newsletterUpdate(newsletterJid, { name: 'New Name' })
await sock.newsletterUpdate(newsletterJid, { description: 'New description' })
await sock.newsletterUpdate(newsletterJid, { picture: buffer })
await sock.newsletterUpdateName(newsletterJid, 'New Name')
await sock.newsletterUpdateDescription(newsletterJid, 'New description')
await sock.newsletterUpdatePicture(newsletterJid, buffer)
await sock.newsletterRemovePicture(newsletterJid)
await sock.newsletterUpdateReactions(newsletterJid, 'ALL')
await sock.newsletterDelete(newsletterJid)
await sock.newsletterChangeOwner(newsletterJid, newOwnerJid)
await sock.newsletterDemote(newsletterJid, userJid)
await sock.newsletterFollow(newsletterJid)
await sock.newsletterUnfollow(newsletterJid)
const subscribed = await sock.newsletterSubscribed()
const sub = await sock.subscribeNewsletterUpdates(newsletterJid)
console.log(sub.duration)
await sock.newsletterMute(newsletterJid)
await sock.newsletterUnmute(newsletterJid)
await sock.newsletterUpdateUserSetting(newsletterJid, 'FOLLOWER_NOTIFICATIONS', true)
await sock.newsletterUpdateUserSetting(newsletterJid, 'ADMIN_NOTIFICATIONS', false)
const count = await sock.newsletterAdminCount(newsletterJid)
const adminInfo = await sock.newsletterAdminInfo(newsletterJid)
console.log(adminInfo.adminCount, adminInfo.adminProfile, adminInfo.adminProfilesEnabled)
const capabilities = await sock.newsletterAdminCapabilities(newsletterJid)
const { canPost, canPostMusic, capabilities: caps } = await sock.newsletterCanPostStatus(newsletterJid)
await sock.newsletterCreateAdminInvite(newsletterJid, userJid)
await sock.newsletterRevokeAdminInvite(newsletterJid, userJid)
await sock.newsletterAcceptAdminInvite(newsletterJid)
const pending = await sock.newsletterPendingAdminInvites(newsletterJid)
const followers = await sock.newsletterFollowers(newsletterJid, { count: 50 })
const subscribers = await sock.newsletterSubscribers(newsletterJid)
const messages = await sock.newsletterFetchMessages(newsletterJid, 20)
const older = await sock.newsletterFetchMessages(newsletterJid, 20, undefined, lastServerId)
const updates = await sock.newsletterFetchMessageUpdates(newsletterJid, { count: 20 })
const paged = await sock.newsletterFetchMessageUpdates(newsletterJid, { count: 20, before: serverId })
await sock.newsletterPinMessages(newsletterJid, serverId)
await sock.newsletterPinMessages(newsletterJid, [serverId1, serverId2])
await sock.newsletterUnpinMessages(newsletterJid, serverId)
await sock.newsletterLabelAiContent(newsletterJid, serverId)
await sock.newsletterLabelPaidPartnership(newsletterJid, serverId)
await sock.newsletterReactMessage(newsletterJid, serverId, '❤️')
await sock.newsletterReactMessage(newsletterJid, serverId, null)
await sock.newsletterBulkReactions(newsletterJid, serverId, '❤️', 10)
await sock.newsletterBulkReactions(newsletterJid, serverId, ['❤️', '🔥'], 20)
await sock.newsletterBulkReactions(newsletterJid, serverId, ['❤️', '🔥', '😂'], 20, { mode: 'random' })
await sock.newsletterBulkReactions(newsletterJid, serverId, ['❤️', '🔥', '😂'], 10, { mode: 'each' })
await sock.newsletterBulkReactions(newsletterJid, serverId, ['❤️', '🔥'], 100, { fake: true })
await sock.newsletterBulkReactions(newsletterJid, serverId, '🔥', 50, { delayMs: 200 })
const reactionSenders = await sock.newsletterReactionSenders(newsletterJid, serverId)
await sock.newsletterSendPollVote(newsletterJid, serverId, 'Option A')
await sock.newsletterSendPollVote(newsletterJid, serverId, ['Option A', 'Option B'])
const voters = await sock.newsletterPollVoters(newsletterJid, serverId, { limit: 100 })
const forOption = await sock.newsletterPollVoters(newsletterJid, serverId, { voteHash: 'abc123' })
const responses = await sock.newsletterQuestionResponses(newsletterJid, serverId, { count: 20 })
const filtered = await sock.newsletterQuestionResponses(newsletterJid, serverId, { filter: 'starred' })
const searched = await sock.newsletterQuestionResponses(newsletterJid, serverId, { searchText: 'hello' })
await sock.newsletterQuestionResponseState(newsletterJid, serverId, responseServerId, 'STARRED')
await sock.newsletterSendStatus(newsletterJid, { text: 'Hello from channel!' })
await sock.newsletterSendStatus(newsletterJid, { image: buffer, caption: 'Caption here' })
await sock.newsletterSendStatus(newsletterJid, { video: buffer, caption: 'Caption here' })
await sock.newsletterSendStatus(newsletterJid, { audio: buffer })
await sock.newsletterSendStatus(newsletterJid, { image: buffer, question: true })
await sock.newsletterSendStatus(newsletterJid, { text: 'My answer' }, {
interactionType: 'question_response',
parentServerId: questionServerId
})
await sock.newsletterSendStatus(newsletterJid, { image: buffer }, {
interactionType: 'question_reshare',
parentServerId: questionServerId,
responseServerId: responseServerId
})
await sock.newsletterSendStatus(newsletterJid, { text: 'AI content' }, { aiContent: true })
await sock.newsletterSendStatus(newsletterJid, { text: 'No attribution' }, { statusAttribution: false })
const sent = await sock.newsletterSendStatus(newsletterJid, { text: 'Hello' })
console.log(sent.newsletterStatusServerId)
await sock.newsletterReactStatus(newsletterJid, serverId, '❤️')
await sock.newsletterReactStatus(newsletterJid, serverId, null)
await sock.newsletterRevokeStatus(newsletterJid, statusId)
const statuses = await sock.newsletterFetchStatus(newsletterJid, { count: 20 })
const before = await sock.newsletterFetchStatus(newsletterJid, { count: 20, before: serverId })
const after = await sock.newsletterFetchStatus(newsletterJid, { count: 20, after: serverId })
const statusUpdates = await sock.newsletterFetchStatusUpdates(newsletterJid, { count: 20 })
const myAddOns = await sock.newsletterMyAddOns()
const myStatusAddOns = await sock.newsletterStatusMyAddOns()
const filtered = await sock.newsletterMyAddOns({ jid: newsletterJid })
const insights = await sock.newsletterInsights(newsletterJid)
const custom = await sock.newsletterInsights(newsletterJid, { metrics: ['NET_FOLLOWS', 'UNFOLLOWS', 'IMPRESSIONS'] })
const enforcements = await sock.newsletterEnforcements(newsletterJid)
const { adminProfiles, profilePictureDeletions, suspensions, violatingMessages, geoSuspensions } = enforcements
const reports = await sock.newsletterReports()
await sock.newsletterAppealReport(reportId, 'REASON_HERE')
const recommended = await sock.newsletterRecommended({ limit: 20 })
const local = await sock.newsletterRecommended({ countryCodes: ['NG', 'US'] })
const results = await sock.newsletterDirectorySearch('football', { limit: 20 })
const directory = await sock.newsletterDirectoryList({ view: 'RECOMMENDED', limit: 20 })
const byCountry = await sock.newsletterDirectoryList({ countryCodes: ['NG'], categories: ['SPORTS'] })
const categories = await sock.newsletterDirectoryCategories({ perCategoryLimit: 10 })
const similar = await sock.newsletterSimilar(newsletterJid, { limit: 20 })
🔧 Builder API
All builders can be used as a fluent chain. Call .send(jid) to send, or .build(jid) to get the proto message without sending.
AIRich Builder
const r = sock.airich()
r.setTitle('Title')
r.setFooter('© zervonex')
r.setBotJid('867051314767696@bot')
r.setResponseId('my-id')
r.setContextInfo({ ... })
r.addText('hello **world**')
r.addCode('javascript', 'console.log("hi")')
r.addTable([['Header1', 'Header2'], ['R1C1', 'R1C2']])
r.addImage('https://example.com/image.jpg')
r.addVideo('https://example.com/video.mp4')
r.addReels([{ creator, avatar_url, thumbnail_url, reels_url }])
r.addPost({ title, username, thumbnail_url, post_url, source_app, likes_count, is_verified })
r.addProduct({ title, brand, price, sale_price, product_url, image_url })
r.addSource([{ title, url, favicon }])
r.addMap([{ latitude, longitude, name, address }])
r.addWidget(ctas, title)
r.addFooterAction({ cta_text, cta_type, cta_url })
r.addMetadata('small info text')
r.addFOAText('# Heading\n## Subheading')
r.addTip('Tip callout text')
r.addDivider()
r.addSpacer(2)
r.addThinkingStatus('title', 'THINKING')
r.addProgressStatus('title', true)
r.addTask({ taskId, title, subtitle, status })
r.addLatex({ expression, imageUrl, width, height, fontHeight, padding })
r.addGenerating('GENERATING')
r.addInlineImage('url', 'center')
r.addSuggest(['Pill 1', 'Pill 2'], false)
r.addSection(rawSectionObject)
r.addEmbeddedScreen(screenObject)
r.addRawSections(sections, submessages)
r.loadFrom(msg)
await r.send(jid, opts)
await r.sendEdit(jid, targetId)
await sock.sendRichGeneration(jid, { text, mediaType, url, status, estimatedMs, itemId, responseId })
await sock.sendUnifiedResponse(jid, sections, submessages, options)
Named node IDs — update specific content after adding:
const r = sock.airich()
r.addText('Loading...', { id: 'status' })
r.addCode('js', '// placeholder', { id: 'code' })
r.addText('Done!', { replace: 'status' })
r.addCode('js', 'console.log("real code")', { replace: 'code' })
await r.send(jid)
Button Builder
const b = sock.button()
b.setTitle('Menu')
b.setSubtitle('Choose an option')
b.setBody('What would you like to do?')
b.setFooter('© zervonex')
b.setImage('https://example.com/image.jpg')
b.setVideo('https://example.com/video.mp4')
b.setDocument('https://example.com/file.pdf', 'file.pdf')
b.setLocation(6.5244, 3.3792, 'Lagos')
b.reply('Quick Reply', 'reply_id')
b.url('Visit Website', 'https://zervonex.com')
b.call('Call Us', '+2341234567890')
b.copy('Copy Code', 'NEXUS2025')
b.openWebview('Open App', 'https://app.zervonex.com')
b.catalog('View Catalog')
b.flow('Start Flow', 'flow_id_here')
b.remind('Set Reminder')
b.cancelReminder('Cancel Reminder')
b.address('Share Address')
b.sendLocation('Share Location')
b.reviewAndPay('Review & Pay')
b.reviewOrder('Review Order')
b.orderDetails('Order Details')
b.paymentStatus('Payment Status')
b.transactionDetails('Transaction Details')
b.select('Choose Option', [{ title: 'Section', rows: [{ title: 'Option 1', id: 'opt1' }] }])
b.setBloksWidget([{ __type__: 'Column', id: 'root', children: [{ __type__: 'Text', text: 'Hello' }] }])
b.setLimitedTimeOffer(Date.now() + 3600000)
b.setBottomSheet({ title: 'Select' })
b.setTapTargetConfiguration({ dismissible: true })
b.legacyButton('Button 1', 'btn1_id')
b.legacyButton('Button 2', 'btn2_id')
b.templateReply('Quick Reply', 'reply_id')
b.templateUrl('Visit', 'https://zervonex.com')
b.templateCall('Call', '+2341234567890')
const card = b.toCard()
await b.send(jid)
Carousel Builder
const c = sock.carousel()
c.setCaption('Our Products')
c.setFooter('Powered by zervonex')
c.setCardType('HSCROLL_CARDS')
c.card(sock.button().setBody('Card 1').reply('Select', 'card1').url('Learn More', 'https://example.com'))
c.card(sock.button().setImage('https://example.com/img.jpg').setBody('Card 2').reply('Select', 'card2'))
await c.send(jid)
Album Builder
const a = sock.album()
a.image('https://example.com/1.jpg', 'Caption for photo 1')
a.image(buffer, 'Caption for photo 2')
a.video('https://example.com/video.mp4', 'Caption for video')
a.add([
{ image: { url: 'https://example.com/3.jpg' }, caption: 'Photo 3' },
{ video: { url: 'https://example.com/4.mp4' }, caption: 'Video 2' }
])
a.setDelay(1500)
await a.send(jid)
VerticalAlbum Builder
Sends a full-width stacked media album using the AIRich unified response format. Unlike the standard Album builder (which sends individual messages), this renders all items in a single rich message.
await sock.sendMessage(jid, {
verticalAlbumMessage: [
{ type: 'image', image: { url: 'https://example.com/1.jpg' } },
{ type: 'video', video: { url: 'https://example.com/2.mp4' }, duration: 10 },
{ type: 'image', image: { url: 'https://example.com/3.jpg' }, width: 1280, height: 720 }
]
})
await sock.sendVerticalAlbumMessage(jid, {
items: [
{ type: 'image', image: { url: 'https://example.com/1.jpg' } },
{ type: 'video', video: { url: 'https://example.com/2.mp4' } }
]
})
GridImage Builder
Sends a grid of images using the GRID_IMAGE protocol submessage type.
await sock.sendMessage(jid, {
gridImageMessage: {
imageUrls: [
{ url: 'https://example.com/1.jpg', width: 600, height: 400 },
{ url: 'https://example.com/2.jpg', width: 600, height: 400 },
{ url: 'https://example.com/3.jpg', width: 600, height: 400 }
]
}
})
await sock.sendGridImageMessage(jid, {
imageUrls: [
'https://example.com/1.jpg',
'https://example.com/2.jpg'
]
})
VideoGrid Builder
Sends a grid of videos using GenAIVideoPrimitive with full delivery metadata support.
await sock.sendMessage(jid, {
videoGridMessage: {
videos: [
{
url: 'https://example.com/1.mp4',
title: 'Video 1',
thumbnailUrl: 'https://example.com/thumb1.jpg',
creator: 'Creator Name',
likes: 1200,
isVerified: true
},
{
url: 'https://example.com/2.mp4',
title: 'Video 2',
thumbnailUrl: 'https://example.com/thumb2.jpg'
}
]
}
})
await sock.sendVideoGridMessage(jid, {
videos: [
{ url: 'https://example.com/1.mp4', title: 'Clip 1' },
{ url: 'https://example.com/2.mp4', title: 'Clip 2' }
]
})
A2UI Builder
Sends a WhatsApp A2UI bloksWidget interactive message. Use the command menu preset or build a fully custom component tree.
await sock.sendA2UIMessage(jid, {
bloksWidget: {
type: 'im_a2ui',
data: JSON.stringify({ version: 'v0.9', createSurface: { } })
},
nativeFlowMessage: { buttons: [{ name: 'single_select', buttonParamsJson: '...' }] }
})
await sock.sendA2UICommandMenu(jid, {
title: 'zervonex Menu',
rows: [
['!help', 'Show all commands'],
['!ping', 'Check bot status'],
['!ai', 'Ask AI a question']
],
buttons: [
{ text: 'GitHub', url: 'https://github.com/nexustechpro2' }
],
footer: 'Powered by zervonex'
})
A2UI row format: [command, description] or { title, description, id }
A2UI button format: { text, url } or { label, url }
Poll Builder
const p = sock.poll()
p.name('What is your favorite color?')
p.options(['Red', 'Blue', 'Green', 'Yellow'])
p.addOption('Purple')
p.multiSelect(2)
p.hideVoter()
p.announcement()
p.quiz('Blue')
p.setEndTime(Math.floor(Date.now() / 1000) + 3600)
p.hideParticipantName()
p.allowAddOption()
await p.send(jid)
Event Builder
const e = sock.event()
e.setName('NexusTech Conference 2026')
e.setDescription('Annual developer meetup')
e.setLocation({ degreesLatitude: 6.5244, degreesLongitude: 3.3792, name: 'Lagos, Nigeria' })
e.setJoinLink('https://meet.example.com/nexusconf')
e.setStartTime(Math.floor(Date.now() / 1000) + 86400)
e.setEndTime(Math.floor(Date.now() / 1000) + 172800)
e.setCanceled(false)
e.setReminder(3600)
e.setScheduledCall(true)
await e.send(jid)
Payment Builder
const pay = sock.payment()
pay.setCurrency('NGN')
pay.setAmount(10000000)
pay.setFrom('1234567890@s.whatsapp.net')
pay.setNote('Payment for order #123')
pay.setExpiry(Date.now() + 86400000)
await pay.send(jid)
Order Builder
const ord = sock.order()
ord.from({
orderId: 'ORDER_001',
itemCount: 3,
totalAmount1000: 45000000,
totalCurrencyCode: 'NGN',
orderTitle: 'My Order',
message: 'Thank you for your order',
thumbnail: 'https://example.com/product.jpg'
})
await ord.send(jid)
🛠 Utility Methods
await sock.requestContactRefresh(['1234567890@s.whatsapp.net', '0987654321@s.whatsapp.net'])
await sock.fetchMessageHistory(50, oldestMessageKey, oldestMessageTimestamp)
await sock.requestPlaceholderResend(messageKey)
const captured = sock.captureAiRich(msg.message)
if (captured) await sock.relayAiRich(targetJid, captured)
📦 Exported Utilities
Everything exported from @zervonex/baileys that you can import directly:
import {
makeWASocket,
useMultiFileAuthState,
useKeyvAuthState,
hasKeyvAuth,
makeCacheableSignalKeyStore,
Browsers,
DisconnectReason,
generateWAMessage,
generateWAMessageFromContent,
generateWAMessageContent,
generateMessageIDV2,
downloadMediaMessage,
downloadContentFromMessage,
prepareWAMessageMedia,
getContentType,
normalizeMessageContent,
extractMessageContent,
getAggregateVotesInPollMessage,
jidDecode,
jidNormalizedUser,
areJidsSameUser,
isJidGroup,
isJidNewsletterGroup,
isJidUser,
isLidUser,
isJidBroadcast,
isJidStatusBroadcast,
WAMessageStubType,
WAMessageStatus,
WAMessageAddressingMode,
resolveStubType,
WA_STUB_TYPE_NAMES,
AIProvenanceType,
DeviceCapabilityLevel,
tokenizeCode,
extractIE,
waitAllPromises,
generateVerificationMetadata,
hasVerifiableProofs,
wrapToBotForwardedMessage,
attachBotVerificationProofs,
buildImaginePrimitive,
prepareRichGenerationMessage,
extractVerificationMetadata
checkStatusWA,
} from '@zervonex/baileys'
📡 Events Reference
connection.update | connection, qr, lastDisconnect, isNewLogin | Connection state changed |
creds.update | partial AuthenticationCreds | Auth credentials updated |
messaging-history.set | chats, contacts, messages, syncType, isLatest | Initial history loaded |
messages.upsert | messages[], type | New message(s) received |
messages.update | [{ key, update }] | Message status/content updated |
messages.delete | keys[] or { jid, all } | Message(s) deleted |
messages.reaction | [{ reaction, key }] | Reaction added/removed |
messages.media-update | [{ key, media?, error? }] | Media upload status updated |
message-receipt.update | [{ key, receipt }] | Read/delivered receipts |
chats.set | { chats[], isLatest } | Initial chat list loaded |
chats.upsert | Chat[] | New chat(s) appeared |
chats.update | Partial<Chat>[] | Chat metadata updated |
chats.delete | string[] | Chat(s) deleted |
contacts.set | { contacts[] } | Initial contacts loaded |
contacts.upsert | Contact[] | New contact(s) added |
contacts.update | Partial<Contact>[] | Contact info updated |
groups.upsert | GroupMetadata[] | New group created/joined |
groups.update | Partial<GroupMetadata>[] | Group metadata changed |
group-participants.update | { id, author, authorPn, participants, action } | Member added/removed/promoted/demoted |
blocklist.set | { blocklist[] } | Initial blocklist loaded |
blocklist.update | { blocklist[], type } | Block/unblock event |
presence.update | { id, presences } | User presence changed |
call | WACallEvent[] | Incoming/outgoing call |
labels.edit | Label | Label created/updated |
labels.association | [{ association, type }] | Message/chat label assigned/removed |
settings.update | { setting, value } | Account setting changed (theme, AI thread rename, device capabilities, etc.) |
group-participants.update fields:
id | string | Group JID |
author | string | JID of who performed the action |
authorPn | string | Phone number of the actor (PN format) |
participants | string[] | JIDs of affected participants |
action | string | add | remove | promote | demote |
🤖 Meta AI Messages
Meta AI bot messages (msmsg encrypted responses) are now automatically decrypted when received. They arrive as regular messages in messages.upsert once decrypted:
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
if (!msg.message) continue
const content = extractMessageContent(msg.message)
if (content?.extendedTextMessage?.text) {
console.log('Message (possibly Meta AI):', content.extendedTextMessage.text)
}
}
})
If decryption fails (e.g. missing message secret), the message is gracefully NACKed and logged — it won't crash your session.
🎯 Best Practices
Reconnection
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
if (connection === 'close') {
const code = lastDisconnect?.error?.output?.statusCode
const shouldReconnect = code !== DisconnectReason.loggedOut
if (shouldReconnect) connectToWhatsApp()
else console.log('Logged out — clear session and re-authenticate')
}
})
Cache Group Metadata
import NodeCache from '@cacheable/node-cache'
const groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false })
const sock = makeWASocket({
cachedGroupMetadata: async (jid) => groupCache.get(jid)
})
sock.ev.on('groups.update', async ([event]) => {
const metadata = await sock.groupMetadata(event.id)
groupCache.set(event.id, metadata)
})
Rate Limiting
const queue = []
let sending = false
const queueMessage = (jid, content) => {
queue.push({ jid, content })
if (!sending) processQueue()
}
const processQueue = async () => {
sending = true
while (queue.length > 0) {
const { jid, content } = queue.shift()
await sock.sendMessage(jid, content)
await new Promise(r => setTimeout(r, 1000))
}
sending = false
}
Error Handling
try {
await sock.sendMessage(jid, { text: 'Hello' })
} catch (error) {
if (error.output?.statusCode === 401) console.log('Unauthorized')
else console.error('Send failed:', error)
}
Poll Decryption
import { getAggregateVotesInPollMessage } from '@zervonex/baileys'
sock.ev.on('messages.update', async (event) => {
for (const { key, update } of event) {
if (update.pollUpdates) {
const pollCreation = await getMessage(key)
if (pollCreation) {
const result = getAggregateVotesInPollMessage({
message: pollCreation,
pollUpdates: update.pollUpdates
})
console.log('Poll votes:', result)
}
}
}
})
⚠️ Disclaimer
This project is not affiliated with WhatsApp or Meta. Use responsibly:
- Follow WhatsApp's Terms of Service
- Do not spam or send unsolicited messages
- Respect user privacy
- Be aware of WhatsApp's rate limits
- This library is for legitimate automation purposes only
🙏 Acknowledgments
- WhiskeySockets for the original Baileys library
- All contributors and the open-source community