@zervonex/baileys
v3.0.3
Published
Advanced WhatsApp Web API built on Baileys — interactive messages, buttons, carousels, products, events, payments, polls, albums, sticker packs and more.
Maintainers
Keywords
Readme
@zervonex/baileys
Advanced WhatsApp Web API — Built on WhiskeySockets/Baileys
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
- Quick Start
- Authentication
- Browser Options
- Socket Configuration
- Session Management
- Sending Messages
- Message Modifications
- Media Download
- Group Management
- User Operations
- Check Number Status
- Privacy Controls
- Reporting
- Reporting Info Store
- Chat Operations
- Newsletter / Channels
- Builder API
- Utility Methods
- Exported Utilities
- Events Reference
- Best Practices
- Disclaimer
📦 Installation
# npm
npm install @zervonex/baileys
# yarn
yarn add @zervonex/baileysDrop-in replacement for @whiskeysockets/baileys — add this to package.json:
{
"dependencies": {
"@zervonex/baileys": "latest"
}
}
Import:
// ESM (recommended)
import makeWASocket from '@zervonex/baileys'
// CommonJS
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)
// Just call it — no need to wait or poll for WS state
if (!sock.authState.creds.registered) {
const code = await sock.requestPairingCode('1234567890') // digits only, no + or spaces
console.log('Pairing code:', code)
}Custom Pairing Code
// Must be exactly 8 characters
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.
| Option | Value | Notes |
|--------|-------|-------|
| 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'
// Pass any browser string as the second argument (Chrome, Safari, Firefox, etc.)
const sock = makeWASocket({
browser: Browsers.ubuntu('Chrome') // recommended
// browser: Browsers.macOS('Safari')
// browser: Browsers.windows('Chrome')
// browser: Browsers.appropriate('Chrome') // auto-detects your server OS
})⚙️ Socket Configuration
Key options from DEFAULT_CONNECTION_CONFIG:
const sock = makeWASocket({
// Required
auth: { creds, keys },
// Browser identity
browser: Browsers.ubuntu('Chrome'),
// Link preview quality (true = upload full image, false = compressed local thumb)
generateHighQualityLinkPreview: true,
// Cache group metadata to avoid repeated fetches
cachedGroupMetadata: async (jid) => groupCache.get(jid),
// Suppress your online status when connecting
markOnlineOnConnect: false,
// Sync full message history on connect
syncFullHistory: false,
// Mark all outgoing messages as AI-generated (global default)
aiLabel: false,
// Logger (use pino({ level: 'silent' }) to suppress logs)
logger: pino({ level: 'silent' }),
// Connection timeout in ms
connectTimeoutMs: 60000,
// Max retries for failed message sends
maxMsgRetryCount: 5,
// Custom message retrieval for retry/poll decryption
getMessage: async (key) => store.loadMessage(key.remoteJid, key.id),
// Opt out of auto-following the official zervonex newsletter on connect
autoFollowNewsletter: false, // default: true
})💾 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, // how many recent prekeys to keep (default: 150)
cleanupThreshold: 50, // new prekeys before a cleanup runs (default: 50)
logger, // optional — logs checksum mismatches and cleanup events
})
const sock = makeWASocket({ auth: state })
sock.ev.on('creds.update', saveCreds)
// Diagnostics
const stats = await getStats()
// { totalFiles: 42, preKeyCount: 150, nextPreKeyId: 3200, lastCleanupAt: '2026-07-05T...' }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 the Keyv namespace, 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'
// ── Redis ─────────────────────────────────────────────────────────────────────
const { state, saveCreds } = await useKeyvAuthState('bot1', 'redis://localhost:6379')
// ── PostgreSQL / Supabase / Neon ──────────────────────────────────────────────
const { state, saveCreds } = await useKeyvAuthState('bot1', 'postgresql://user:pass@localhost/db')
// ── MongoDB ───────────────────────────────────────────────────────────────────
const { state, saveCreds } = await useKeyvAuthState('bot1', 'mongodb://localhost/baileys')
// ── MySQL / PlanetScale ───────────────────────────────────────────────────────
const { state, saveCreds } = await useKeyvAuthState('bot1', 'mysql://user:pass@localhost/db')
// ── SQLite (local, zero-config) ───────────────────────────────────────────────
const { state, saveCreds } = await useKeyvAuthState('bot1', 'sqlite://auth.db')
// ── etcd ──────────────────────────────────────────────────────────────────────
const { state, saveCreds } = await useKeyvAuthState('bot1', 'etcd://localhost:2379')
// ── Memcache ──────────────────────────────────────────────────────────────────
const { state, saveCreds } = await useKeyvAuthState('bot1', 'memcache://localhost:11211')
// ── In-memory (testing / ephemeral — data lost on restart) ───────────────────
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') // build once, share across sessions
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', // instead of the default "keyv" collection
})Per-type backend routing (e.g. prekeys on SQLite, sessions on Postgres) is not currently supported — one
useKeyvAuthStatecall uses one backend for every key type.
Clearing a session (logout)
const { state, saveCreds, clearSession } = await useKeyvAuthState('bot1', 'redis://localhost:6379')
// on logout / when you want to wipe this session's auth entirely:
await clearSession()clearSession() wipes every key belonging to that sessionId — creds, prekeys, sessions, sender keys, everything — using each backend's native namespace-scoped bulk delete (a real deleteMany/DELETE WHERE/SCAN+delete under the hood, not a per-key loop). It only affects this session; other sessions sharing the same backend are untouched. There's no equivalent method on useMultiFileAuthState — delete the session folder instead.
Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| 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
| | useMultiFileAuthState | useKeyvAuthState |
|---|---|---|
| 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.
// JID formats
'[email protected]' // personal chat
'[email protected]' // group
'status@broadcast' // story/status
'120363...@newsletter' // newsletter channelContent Key Reference
Every key you can pass to sock.sendMessage:
| Key | Description |
|-----|-------------|
| 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
// Plain text
await sock.sendMessage(jid, { text: 'Hello World!' })
// With link preview (auto-detects URLs in text)
await sock.sendMessage(jid, { text: 'Check this out: https://github.com' })
// Quote/reply
await sock.sendMessage(jid, { text: 'This is a reply' }, { quoted: message })
// Mention users
await sock.sendMessage(jid, {
text: '@1234567890 hey!',
mentions: ['[email protected]']
})
// AI-generated label — marks the message as bot-generated
// content flag (per-message)
await sock.sendMessage(jid, { text: 'AI response here', ai: true })
// options flag (per-message)
await sock.sendMessage(jid, { text: 'AI response here' }, { ai: true })
// or use aiLabel alias
await sock.sendMessage(jid, { text: 'AI response here' }, { aiLabel: true })
// global config (applies to every message from this socket)
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).
| Mode | Your Main | Your Companions | Recipient Main | Recipient Companions |
|------|-----------|-----------------|----------------|----------------------|
| toAll | ✅ | ✅ | ✅ | ✅ |
| toMainOnly | ✅ | ❌ | ✅ | ❌ |
| toRecipientMain | ✅ | ✅ | ✅ | ❌ |
| toSelf | ✅ | ✅ | ❌ | ❌ |
| toRecipient | ❌ | ❌ | ✅ | ✅ |
| toSelfMain | ✅ | ❌ | ❌ | ❌ |
| toRecipientMainOnly | ❌ | ❌ | ✅ | ❌ |
// Everyone gets it (default)
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toAll' })
// Main phones only — no companion devices on either side
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toMainOnly' })
// Recipient's main phone only — your companions still get it
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toRecipientMain' })
// Your devices only — recipient gets nothing
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toSelf' })
// Recipient all devices — you see nothing
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toRecipient' })
// Your main phone only — nothing else
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toSelfMain' })
// Recipient main phone only — you see nothing
await sock.sendMessage(jid, { text: 'Hello!' }, { deliveryMode: 'toRecipientMainOnly' })
deliveryModeis optional — omitting it defaults totoAlland behaves exactly like normal.
Media Messages
You can pass
{ url: '...' }, aBuffer, a file path string, or{ stream: Stream }for any media type.
Image
// From URL
await sock.sendMessage(jid, {
image: { url: 'https://example.com/image.jpg' },
caption: 'Caption here'
})
// From Buffer
await sock.sendMessage(jid, { image: buffer, caption: 'From buffer' })
// From file
await sock.sendMessage(jid, { image: fs.readFileSync('./image.jpg') })
// View once
await sock.sendMessage(jid, { image: { url: './secret.jpg' }, viewOnce: true })Video
await sock.sendMessage(jid, {
video: { url: './video.mp4' },
caption: 'Watch this!',
gifPlayback: false, // true for GIF
ptv: false // true for video note (circle)
})Audio
await sock.sendMessage(jid, {
audio: { url: './audio.mp3' },
mimetype: 'audio/mp4',
ptt: true // true = voice message, false = audio file
})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: '💖', // empty string removes reaction
key: message.key
}
})Poll
// Standard poll
await sock.sendMessage(jid, {
poll: {
name: 'Favorite Color?',
values: ['Red', 'Blue', 'Green'],
selectableOptionsCount: 1,
toAnnouncementGroup: false
}
})
// Quiz poll (single correct answer)
await sock.sendMessage(jid, {
poll: {
name: 'What is 2+2?',
values: ['3', '4', '5'],
selectableOptionsCount: 1,
pollType: 1, // 1 = quiz
correctAnswer: '4'
}
})
// Timed poll
await sock.sendMessage(jid, {
poll: {
name: 'Quick vote!',
values: ['Yes', 'No'],
endTime: Math.floor(Date.now() / 1000) + 3600, // 1 hour
hideParticipantName: true,
allowAddOption: false
}
})
// Or use the Poll builder — see Builder API section
await sock.poll()
.name('Favorite Color?')
.options(['Red', 'Blue', 'Green'])
.send(jid)Pin Message
await sock.sendMessage(jid, {
pin: {
type: 1, // 0 to unpin
time: 86400, // 24h = 86400 | 7d = 604800 | 30d = 2592000
key: message.key
}
})Keep Message
await sock.sendMessage(jid, {
keep: message.key,
type: 1, // 2 to unkeep
time: 86400
})Disappearing Messages
// Enable (seconds: 86400 = 24h, 604800 = 7d, 7776000 = 90d)
await sock.sendMessage(jid, { disappearingMessagesInChat: 86400 })
// Disable
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: '[email protected]' },
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: '[email protected]' },
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: '[email protected]' },
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: '[email protected]' },
message: { conversation: 'Original status text' }
}
})Key Fields
| Field | Description |
|-------|-------------|
| 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
| Type | name value | Required params |
|------|-------------|-----------------|
| 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({ /* optional advanced config */ }),
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
// Without media header
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 })
// With image header
await sock.sendMessage(jid, {
image: { url: 'https://example.jpg' }, // or buffer
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 })
// With product header
await sock.sendMessage(jid, {
product: {
productImage: { url: 'https://example.jpg' }, // or buffer
productImageCount: 1,
title: 'Product Title',
description: 'Product Description',
priceAmount1000: 20000 * 1000,
currencyCode: 'USD',
retailerId: 'Retail',
url: 'https://example.com'
},
businessOwnerJid: '[email protected]',
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:
// Text with classic buttons
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
})
// Image header with classic buttons
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:
| Value | Type | Description |
|-------|------|-------------|
| 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:
| Value | Type | Description |
|-------|------|-------------|
| 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
// via sendMessage
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 })
// via shorthand
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)
// via builder — see Builder API section
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
// via sendMessage
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' }) }
]
}
}
]
}
})
// via builder — see Builder API section
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
// via sendMessage
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 // remind 1 hour before
}
})
// via builder — see Builder API section
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
// via sendMessage
await sock.sendMessage(jid, {
requestPaymentMessage: {
currency: 'NGN',
amount: 10000000,
from: '[email protected]',
note: 'Payment for order #123'
}
})
// via builder
await sock.payment()
.setCurrency('NGN')
.setAmount(10000000)
.setFrom('[email protected]')
.setNote('Payment for order #123')
.send(jid)Order
// via sendMessage
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' }
}
})
// via builder
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.
// via sendMessage
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 } })
// via shorthand
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 })
// Media also accepts Buffer or file path
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.
// Text
await sock.sendMessage('status@broadcast', { text: 'My status update!' })
// Image
await sock.sendMessage('status@broadcast', { image: { url: 'https://example.com/image.jpg' }, caption: 'Caption' })
// Video
await sock.sendMessage('status@broadcast', { video: { url: 'https://example.com/video.mp4' }, caption: 'Caption' })
// Audio
await sock.sendMessage('status@broadcast', { audio: { url: 'https://example.com/audio.mp3' }, ptt: false })
// Send to specific contacts only
await sock.sendMessage('status@broadcast', {
text: 'Private status',
statusJidList: [
'[email protected]',
'[email protected]'
]
})React to a Status
// Method 1 — via sendMessage
await sock.sendMessage('status@broadcast', {
react: {
text: '❤️',
key: message.key // the key of the status message
}
}, {
statusJidList: [message.key.participant] // the person who posted the status
})
// Method 2 — via sendReaction shorthand
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.
// Text
await sock.sendStatusMentions({ text: 'Hey @someone check this out!' }, ['[email protected]'])
// Image
await sock.sendStatusMentions({ image: { url: 'https://example.com/image.jpg' }, caption: 'Caption' }, ['[email protected]'])
// Video
await sock.sendStatusMentions({ video: { url: 'https://example.com/video.mp4' } }, ['[email protected]'])
// Audio
await sock.sendStatusMentions({ audio: { url: 'https://example.com/audio.mp3' }, ptt: true }, ['[email protected]'])
// Tag a group (all members get notified)
await sock.sendStatusMentions({ text: 'Hey group!' }, ['[email protected]'])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.
// Method 1 — via sendMessage
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 // optional — defaults to first sticker
}
}, { quoted: message })
// Method 2 — dedicated shorthand
await sock.stickerPackMessage(jid, {
name: 'My Stickers',
publisher: 'zervonex',
stickers: [
{ data: buffer, emojis: ['😊'] },
{ data: './sticker.png', emojis: ['😂'] }
],
cover: coverBuffer
}, { quoted: message })Per-sticker options:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| 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):
| Value | Description |
|-------|-------------|
| 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:
| Format | Support | Notes | |--------|---------|-------| | 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.sendMessagewith anaiRichcontent keysock.airich()fluent builder — chain methods and call.send(jid)
Code Block
// via sendMessage
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 })
// via builder
await sock.airich()
.setTitle('Code Example')
.addCode('javascript', `const greet = name => \`Hello, \${name}!\``)
.send(jid)
// via shorthand
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
// via sendMessage
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 })
// via builder
await sock.airich()
.setTitle('Comparison')
.addTable([
['Feature', 'Java', 'JavaScript'],
['Type', 'Compiled', 'Interpreted'],
['Typing', 'Static', 'Dynamic']
])
.send(jid)
// via shorthand
await sock.sendTable(jid, 'Java vs JavaScript', ['Feature', 'Java', 'JavaScript'], [
['Type', 'Compiled', 'Interpreted'],
['Typing', 'Static', 'Dynamic']
], quoted, { footer: 'Hope this helps!' })
// sendTableV2 — pipe-delimited string format
await sock.sendTableV2(jid, [
'Java vs JavaScript',
'Feature | Java | JavaScript',
'Type | Compiled | Interpreted;;Typing | Static | Dynamic'
], quoted, { footer: 'Hope this helps!' })
// sendList — two-column key/value style
await sock.sendList(jid, 'Bot Info', [
['Name', 'zervonex'],
['Version', '3.0.2'],
['Developer', 'zervonex']
], quoted, { footer: '© zervonex' })Text
// via sendMessage
await sock.sendMessage(jid, {
aiRich: {
text: '## Hello\n\nThis supports **markdown**:\n\n- Bold\n- Lists\n- Headers',
footer: '_Powered by zervonex_'
}
}, { quoted: message })
// multiple text blocks
await sock.sendMessage(jid, {
aiRich: {
texts: ['## Title', 'First paragraph', 'Second paragraph'],
footer: '_zervonex_'
}
}, { quoted: message })
// via builder
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
// via sendMessage
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 })
// via builder
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)
// via shorthand
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
// via sendMessage
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 })
// via builder
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)Suggestions, Widgets, Footer Actions
// via builder
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
// via builder
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)
// Task queue
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() // horizontal line
.addText('*Section B*')
.addDivider('DOT') // dotted divider
.addText('*Section C*')
.addSpacer(3) // 3-line gap
.addText('*Section D*')
.send(jid)LaTeX
// via sendMessage
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 })
// via builder
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)
// via shorthand
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
// Imagine placeholder (generation state)
await sock.airich()
.addText('Generating your image...')
.addImagine({ mediaType: 'image', status: 'GENERATING', estimatedMs: 5000 })
.send(jid)
// Standalone generation message with itemId for later edit
const { msg, responseId, itemId } = await sock.sendRichGeneration(jid, {
text: 'Generating...',
mediaType: 'video',
status: 'GENERATING',
estimatedMs: 8000
})
// Later — edit that same message with the finished result
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
// Load an existing AIRich message back into the builder for editing
const r = sock.airich()
r.loadFrom(receivedMessage.message)
r.addText('Additional content added!')
await r.send(jid)
// Edit a sent AIRich message
const sent = await sock.airich().addText('Original text').send(jid)
await sock.airich()
.addText('Updated text')
.sendEdit(jid, sent.key.id)Capture & Relay
// Capture a received AIRich message for forwarding
const captured = sock.captureAiRich(msg.message)
// Relay it to another jid
if (captured) await sock.relayAiRich(targetJid, captured)Full Combo Example
// via sendMessage — everything in one object
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 })
// via builder — full combo
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)
// via shorthand
await sock.sendRichMessage(jid, {
text: 'Hello from sendRichMessage!',
code: 'console.log("works!")',
language: 'javascript',
footer: '_zervonex_'
}, quoted)aiRich Options Reference
| Key | Type | Description |
|-----|------|-------------|
| 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 })
// ── AI Rich Messages ──────────────────────────────────────────────────────────
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
// Edit
await sock.sendMessage(jid, { text: 'Updated text', edit: originalMessage.key })
// Delete for everyone
const sent = await sock.sendMessage(jid, { text: 'hello' })
await sock.sendMessage(jid, { delete: sent.key })
// Pin (time in seconds: 86400=24h, 604800=7d, 2592000=30d)
await sock.sendMessage(jid, { pin: { type: 1, time: 86400, key: message.key } })
// Unpin
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)
}
})
// Re-upload expired media
await sock.updateMediaMessage(msg)👥 Group Management
// Create group
const group = await sock.groupCreate('Group Name', ['[email protected]'])
// Add / Remove / Promote / Demote
await sock.groupParticipantsUpdate(groupJid, ['[email protected]'], 'add')
await sock.groupParticipantsUpdate(groupJid, ['[email protected]'], 'remove')
await sock.groupParticipantsUpdate(groupJid, ['[email protected]'], 'promote')
await sock.groupParticipantsUpdate(groupJid, ['[email protected]'], 'demote')
// Update group info
await sock.groupUpdateSubject(groupJid, 'New Group Name')
await sock.groupUpdateDescription(groupJid, 'New description')
// Settings
await sock.groupSettingUpdate(groupJid, 'announcement') // only admins can send
await sock.groupSettingUpdate(groupJid, 'not_announcement') // everyone can send
await sock.groupSettingUpdate(groupJid, 'locked') // only admins edit info
await sock.groupSettingUpdate(groupJid, 'unlocked') // everyone edits info
// Metadata
const metadata = await sock.groupMetadata(groupJid)
// Invite link
const code = await sock.groupInviteCode(groupJid)
console.log(`https://chat.whatsapp.com/${code}`)
await sock.groupRevokeInvite(groupJid) // revoke invite link
await sock.groupRevokeInviteV4(groupJid, '[email protected]') // revoke a specific user's pending invite
await sock.groupAcceptInvite('INVITE_CODE') // join group
await sock.groupLeave(groupJid) // leave groupAI Groups
WhatsApp AI Groups are a special group type with Meta AI built-in as a participant.
// Create an AI group
const group = await sock.aiGroupCreate('AI Study Group', ['[email protected]'])
// Get AI group metadata
const metadata = await sock.aiGroupMetadata(groupJid)
// Add the Meta AI bot to an existing AI group
await sock.aiGroupAddBot(groupJid)
// Add / Remove / Promote / Demote participants
await sock.aiGroupParticipantsUpdate(groupJid, ['[email protected]'], 'add')
await sock.aiGroupParticipantsUpdate(groupJid, ['[email protected]'], 'remove')
await sock.aiGroupParticipantsUpdate(groupJid, ['[email protected]'], 'promote')
await sock.aiGroupParticipantsUpdate(groupJid, ['[email protected]'], 'demote')
// Update subject
await sock.aiGroupUpdateSubject(groupJid, 'New AI Group Name')
// Invite link
const code = await sock.aiGroupInviteCode(groupJid)
await sock.aiGroupRevokeInvite(groupJid)
await sock.aiGroupAcceptInvite('INVITE_CODE')
// Leave
await sock.aiGroupLeave(groupJid)
// Settings (announcement, not_announcement, locked, unlocked)
await sock.aiGroupSettingUpdate(groupJid, 'announcement')
// Ephemeral messages
await sock.aiGroupToggleEphemeral(groupJid, 86400) // 24h
await sock.aiGroupToggleEphemeral(groupJid, 0) // disable📱 User Operations
// Check if number exists on WhatsApp
const [result] = await sock.onWhatsApp('1234567890')
if (result?.exists) console.log(result.jid)
// Profile picture
const url = await sock.profilePictu