@pasqua-baileys/baileys
v2.8.3
Published
Baileys is a WebSockets library for interacting with WhatsApp Web — PASQUA edition with anti-ban system, ban checker API, rich messages, interactive messages, albums and more
Maintainers
Keywords
Readme
Table of Contents
| # | Section | Description | |---|---------|-------------| | 1 | Overview | What this package is and what sets it apart | | 2 | Installation | NPM, GitHub, ESM & CJS imports | | 3 | Quick Start | Minimal working connection example | | 4 | Authentication | Auth states, pairing codes, SQLite | | 5 | Sending Messages | Text, mentions, reactions, contacts, locations, events, polls, forward, edit | | 6 | Media Messages | Images, videos, audio, documents, stickers, albums | | 7 | Interactive Messages | Buttons, lists, native flows, carousels, hydrated templates | | 8 | Rich Content | Code blocks, tables, inline entities, rich response arrays | | 9 | Meta AI Features | Meta typing, meta compositing, replay planning | | 10 | Welcome Flow | Auto-greet new contacts with FAQ menus | | 11 | Payment Messages | Invites, invoices, orders, payment requests | | 12 | Message Options | AI icon, ephemeral, spoilers, view once, raw, and more | | 13 | Status & Stories | Post status updates with text, media, mentions | | 14 | Favorites | Manage WhatsApp Favorites list | | 15 | Newsletter Management | Create, update, follow, react, delete newsletters | | 16 | Group Management | Create, settings, participants, invites, metadata | | 17 | Community Management | Communities, subgroups, linked groups | | 18 | Profile & Business | Profile pictures, business catalog, products, quick replies | | 19 | Privacy & Security | Last seen, online, status, read receipts, calls | | 20 | Utility Methods | LID mapping, dirty bits, app state sync, props | | 21 | AI Groups | Tag groups as Meta AI groups | | 22 | WA-Web Chat Features | Status privacy, AI bots, chat blocking, push config, companions | | 23 | Interop, Username & Account Layers | Matrix interop, usernames, registration, cache-manager auth, anti-ban | | 24 | Events Reference | Full list of socket event listeners | | 25 | Image Processing | Sharp, NAPI-RS, Jimp auto-detection | | 26 | Condition of Use | Channel follow agreement | | 27 | Credits | Attribution and enhancements |
Overview
@pasqua-baileys/baileys is a powerful, production-ready WhatsApp API wrapper for Node.js, built on top of the Baileys protocol. It extends the core with rich messaging capabilities, Meta AI-style compositing, and a streamlined developer experience.
What Sets It Apart
| Feature | Status | Notes |
|---------|--------|-------|
| Rich Messages | ✅ | Code blocks, tables, inline entities, carousel cards |
| Meta AI Style | ✅ | Live thinking indicators without "edited" badges |
| Welcome Flow | ✅ | Auto-greet new contacts with interactive FAQ menus |
| Status Posting | ✅ | Text with colors/fonts, image, video, audio, mentions |
| Favorites Management | ✅ | Add/remove JIDs with local persistence |
| HD Profile Picture | ✅ | Full-size upload with no crop or resize via { hd: true } (max 720px) |
| Rich Preview | 🆕 | Auto-fetch link preview (title, description, image) for any URL via { richPreview: true } |
| Verified Badge | 🆕 | Image/video forward badge via { verifiedMe: true } |
| Like This | 🆕 | Raw relay bypassing all processing via { likeThis: true } |
| GIF Playback | 🆕 | Send videos as GIFs with { gifPlayback: true } |
| LID/PN Resolution | ✅ | Cross-resolve LID and phone number JIDs |
| Member Labels | ✅ | Group participant labels |
| No Obfuscation | ✅ | Fully readable, auditable source code |
| Newsletter Ready | ✅ | Full media support, quoting, quiz polls |
| Image Processing | ✅ | Auto-detects sharp, @napi-rs/image, or jimp |
| Safe FFmpeg | ✅ | Uses spawn instead of exec |
| In-Memory Store | ✅ | Reintroduced with minimal ESM adaptation |
Installation
# NPM
npm install @pasqua-baileys/baileys
# GitHub
npm install github:pasqua-baileys/baileysImport (ESM & CJS)
// ESM
import { makeWASocket } from '@pasqua-baileys/baileys'
// CJS (Node.js 24+)
const { makeWASocket } = require('@pasqua-baileys/baileys')Quick Start
import { makeWASocket, delay, DisconnectReason, useMultiFileAuthState } from '@pasqua-baileys/baileys'
import { Boom } from '@hapi/boom'
import pino from 'pino'
const myPhoneNumber = '6288888888888'
const logger = pino({ level: 'silent' })
const connectToWhatsApp = async () => {
const { state, saveCreds } = await useMultiFileAuthState('session')
const sock = makeWASocket({
logger,
auth: state
})
sock.ev.on('creds.update', saveCreds)
sock.ev.on('connection.update', async (update) => {
const { connection, lastDisconnect } = update
if (connection === 'connecting' && !sock.authState.creds.registered) {
await delay(1500)
const code = await sock.requestPairingCode(myPhoneNumber)
console.log('Pairing code:', code)
}
else if (connection === 'close') {
const shouldReconnect = new Boom(lastDisconnect?.error)?.output?.statusCode !== DisconnectReason.loggedOut
console.log('Connection closed, reconnecting:', shouldReconnect)
if (shouldReconnect) connectToWhatsApp()
}
else if (connection === 'open') {
console.log('Connected to WhatsApp')
}
})
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
if (!msg.message) continue
await sock.sendMessage(msg.key.remoteJid, { text: 'Hello world!' })
}
})
}
connectToWhatsApp()Authentication
Multi-File Auth State (Recommended)
import { useMultiFileAuthState } from '@pasqua-baileys/baileys'
const { state, saveCreds } = await useMultiFileAuthState('session')Single-File Auth State (Experimental)
import { useSingleFileAuthState } from '@pasqua-baileys/baileys'
const { state, saveCreds } = await useSingleFileAuthState('session.json')
// Already includes internal caching — no need for makeCacheableSignalKeyStoreSQLite Auth State (Experimental)
import { useSqliteAuthState } from '@pasqua-baileys/baileys'
const { state, saveCreds } = await useSqliteAuthState('session.db')Custom Pairing Code
const phoneNumber = '6281111111111'
const customCode = 'STARFALL'
await sock.requestPairingCode(phoneNumber, customCode)
console.log('Pairing code:', customCode)Sending Messages
Text & Mentions
// Plain text
sock.sendMessage(jid, { text: 'Hello!' }, { quoted: message })
// With link preview
const url = 'https://www.npmjs.com/package/@pasqua-baileys/baileys'
sock.sendMessage(jid, {
text: url + ' Check it out!',
linkPreview: {
'matched-text': url,
title: '@pasqua-baileys/baileys',
description: 'WhatsApp API for Node.js',
previewType: 0,
jpegThumbnail: fs.readFileSync('./thumb.jpg')
}
})
// Large link preview with favicon
import { prepareWAMessageMedia } from '@pasqua-baileys/baileys'
const { imageMessage: image } = await prepareWAMessageMedia(
{ image: { url: './thumb.jpg' } },
{ upload: sock.waUploadToServer, mediaTypeOverride: 'thumbnail-link' }
)
image.height = 720
image.width = 480
sock.sendMessage(jid, {
text: url + ' Check it out!',
linkPreview: {
'matched-text': url,
title: '@pasqua-baileys/baileys',
description: 'WhatsApp API for Node.js',
previewType: 0,
jpegThumbnail: fs.readFileSync('./thumb.jpg'),
highQualityThumbnail: image,
linkPreviewMetadata: {
linkMediaDuration: 0,
socialMediaPostType: 1 // 0=NONE, 1=REEL, 2=LIVE, 3=LONG, 4=IMAGE, 5=CAROUSEL
}
},
favicon: { url: './favicon.ico' }
})
// Mention specific users
sock.sendMessage(jid, {
text: 'Hello @628123456789',
mentions: ['[email protected]']
})
// Mention all group participants
sock.sendMessage(jid, {
text: 'Hello @all',
mentionAll: true
})Reactions & Pins
// Reaction
sock.sendMessage(jid, {
react: { key: message.key, text: '✨' }
})
// Pin message (86400=1d, 604800=7d, 2592000=30d)
sock.sendMessage(jid, {
pin: message.key,
time: 86400,
type: 1 // 1=pin, 2=unpin
})
// Keep chat (disappearing messages only)
sock.sendMessage(jid, {
keep: message.key,
type: 1 // 1=keep, 2=remove
})Contacts & Locations
// Contact card
const vcard = 'BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nORG:Company;\nTEL;type=CELL;type=VOICE;waid=628123456789:+62 8123 4567 89\nEND:VCARD'
sock.sendMessage(jid, {
contacts: {
displayName: 'John Doe',
contacts: [{ vcard }]
}
})
// Location
sock.sendMessage(jid, {
location: {
degreesLatitude: 24.121231,
degreesLongitude: 55.1121221,
name: 'I am here'
}
})
// Group invite
const inviteCode = groupUrl.split('chat.whatsapp.com/')[1]?.split('?')[0]
sock.sendMessage(jid, {
groupInvite: {
inviteCode,
inviteExpiration: Date.now() + 86400000,
text: 'Join our group!',
jid: groupJid,
subject: groupName
}
})Events & Polls
// Calendar event
sock.sendMessage(jid, {
event: {
name: 'Meet & Mingle Party',
description: 'A fun gathering to connect and chat.',
call: 'audio', // or 'video'
startDate: new Date(Date.now() + 3600000),
endDate: new Date(Date.now() + 28800000),
isCancelled: false,
isScheduleCall: false,
extraGuestsAllowed: false,
location: {
name: 'Jakarta',
degreesLatitude: -6.2,
degreesLongitude: 106.8
}
}
})
// Poll
sock.sendMessage(jid, {
poll: {
name: 'Voting time',
values: ['Yes', 'No'],
selectableCount: 1,
toAnnouncementGroup: false,
endDate: new Date(Date.now() + 28800000),
hideVoter: false,
canAddOption: false
}
})
// Quiz (newsletter only)
sock.sendMessage('1211111111111@newsletter', {
poll: {
name: 'Quiz',
values: ['Yes', 'No'],
correctAnswer: 'Yes',
pollType: 1
}
})
// Poll result
sock.sendMessage(jid, {
pollResult: {
name: 'Poll Result',
votes: [
{ name: 'Nice', voteCount: 10 },
{ name: 'Nah', voteCount: 2 }
],
pollType: 0
}
})
// Poll update
sock.sendMessage(jid, {
pollUpdate: {
metadata: {},
key: message.key,
vote: { enclv: buffer, encPayload: buffer }
}
})Forward & Edit
// Forward
sock.sendMessage(jid, {
forward: message,
force: true
})
// Delete
sock.sendMessage(jid, { delete: message.key })
// Edit text
sock.sendMessage(jid, {
text: 'Updated text!',
edit: message.key
})
// Edit media caption
sock.sendMessage(jid, {
caption: 'Updated caption!',
edit: message.key
})Media Messages
Images & Videos
// Image
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Check this out!'
})
// Video
sock.sendMessage(jid, {
video: { url: './video.mp4' },
gifPlayback: false, // true = send as GIF
ptv: false, // true = send as PTV
caption: 'Watch this!'
})
// ── NEW: GIF PLAYBACK ──
// Send any video as a GIF (autoplays, loops, no sound)
sock.sendMessage(jid, {
video: { url: 'https://https://example.com/demo.mp4' },
gifPlayback: true,
caption: 'Check out this GIF!'
})
// With local file
sock.sendMessage(jid, {
video: { url: './animation.mp4' },
gifPlayback: true,
caption: 'My animated GIF'
})
// With buffer
const videoBuffer = fs.readFileSync('./animation.mp4')
sock.sendMessage(jid, {
video: videoBuffer,
gifPlayback: true
})
// PTV (Portable Touch Video - voice/video note style)
sock.sendMessage(jid, {
video: { url: './video.mp4' },
ptv: true,
caption: 'PTV message'
})Audio & Documents
// Audio
sock.sendMessage(jid, {
audio: { url: './audio.mp3' },
ptt: false // true = voice note
})
// Document
sock.sendMessage(jid, {
document: { url: './file.pdf' },
mimetype: 'application/pdf',
caption: 'My document'
})Stickers & Albums
// Sticker
sock.sendMessage(jid, {
sticker: { url: './sticker.webp' }
})
// Album (images + videos)
sock.sendMessage(jid, {
album: [
{ image: { url: './img1.jpg' }, caption: 'First image' },
{ video: { url: './vid1.mp4' }, caption: 'First video' },
{ image: { url: './img2.jpg' }, caption: 'Second image' }
]
})
// Sticker pack
sock.sendMessage(jid, {
cover: { url: './cover.webp' },
stickers: [
{ data: { url: './sticker1.webp' } },
{ data: { url: './sticker2.webp' } }
],
name: 'My Sticker Pack',
publisher: 'PASQUA',
description: '@pasqua-baileys/baileys'
})Interactive Messages
Buttons & Lists
// Buttons
sock.sendMessage(jid, {
text: 'Choose an option!',
footer: '@pasqua-baileys/baileys',
buttons: [
{ text: 'Sign Up', id: '#SignUp' }
]
})
// Buttons with media and native flow
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Interactive buttons!',
footer: '@pasqua-baileys/baileys',
buttons: [
{ text: 'Rating', id: '#Rating' },
{
text: 'Select',
sections: [
{
title: 'Section 1',
rows: [
{ header: '', title: 'Secret Ingredient', description: '', id: '#SecretIngredient' }
]
},
{
title: 'Section 2',
highlight_label: 'Popular',
rows: [
{ header: '', title: 'Coupon', description: '', id: '#CouponCode' }
]
}
]
}
]
})
// List (private chat only)
sock.sendMessage(jid, {
text: 'List menu!',
footer: '@pasqua-baileys/baileys',
buttonText: 'Select',
title: 'Hello',
sections: [
{
title: 'Menu 1',
rows: [
{ title: 'AI', description: '', rowId: '#AI' }
]
},
{
title: 'Menu 2',
rows: [
{ title: 'Search', description: '', rowId: '#Search' }
]
}
]
})Native Flows & Carousels
// Native flow with options
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Interactive!',
footer: '@pasqua-baileys/baileys',
optionText: 'Select Options',
optionTitle: 'Select Options',
offerText: 'New Coupon!',
offerCode: 'SAVE20',
offerUrl: 'https://example.com',
offerExpiration: Date.now() + 3600000,
nativeFlow: [
{ text: 'Greeting', id: '#Greeting', icon: 'review' },
{ text: 'Call', call: '628123456789' },
{ text: 'Copy', copy: '@pasqua-baileys/baileys' },
{ text: 'Source', url: 'https://example.com', useWebview: true },
{
text: 'Select',
sections: [
{
title: 'Section 1',
rows: [
{ header: '', title: 'Coupon', description: '', id: '#CouponCode' }
]
}
],
icon: 'default'
}
],
interactiveAsTemplate: false
})
// Carousel with cards
sock.sendMessage(jid, {
text: 'Carousel!',
footer: '@pasqua-baileys/baileys',
cards: [
{
image: { url: './img1.jpg' },
caption: 'Image 1',
footer: 'Pinterest',
nativeFlow: [
{ text: 'Source', url: 'https://example.com', useWebview: true }
]
},
{
image: { url: './img2.jpg' },
caption: 'Image 2',
footer: 'Pinterest',
offerText: 'New Coupon!',
offerCode: 'SAVE20',
offerUrl: 'https://example.com',
offerExpiration: Date.now() + 3600000,
nativeFlow: [
{ text: 'Source', url: 'https://example.com' }
]
}
]
})
// Native flow with audio footer
sock.sendMessage(jid, {
text: 'Music in footer!',
audioFooter: { url: './audio.mp3' },
nativeFlow: [
{ text: 'Good, next', id: '#Next', icon: 'review' },
{ text: 'Skip', id: '#Skip', icon: 'default' }
]
})Hydrated Templates
sock.sendMessage(jid, {
title: 'Hello',
image: { url: './image.jpg' },
caption: 'Template!',
footer: '@pasqua-baileys/baileys',
templateButtons: [
{ text: 'Tap Here', id: '#Order' },
{ text: 'Source', url: 'https://example.com' },
{ text: 'Call', call: '628123456789' }
]
})Rich Content
Code Blocks
// Built-in tokenizer
sock.sendMessage(jid, {
disclaimerText: 'Code Block',
headerText: '## Example Usage',
contentText: '---',
code: 'console.log("Hello, World!")',
language: 'javascript',
footerText: 'Pretty simple, right?'
})
// Supported languages: css, html, javascript, typescript, python, golang, rust, c, c#, c++, bash, bat, powershell
// Manual tokenization
import { tokenizeCode } from '@pasqua-baileys/baileys'
const language = 'javascript'
const code = 'console.log("Hello, World!")'
sock.sendMessage(jid, {
disclaimerText: 'Tokenized Code',
richResponse: [
{ text: 'Example Usage' },
{ language, code: tokenizeCode(code, language) },
{ text: 'Pretty simple, right?' }
]
})Tables & Inline Entities
// Table message
sock.sendMessage(jid, {
disclaimerText: 'Table',
headerText: '## Comparison',
contentText: '---',
title: 'Runtime Comparison',
table: [
['', 'Node.js', 'Bun', 'Deno'],
['Engine', 'V8', 'JavaScriptCore', 'V8'],
['Performance', '4/5', '5/5', '4/5']
],
noHeading: false,
footerText: 'Does this help?'
})
// Inline entities (links)
sock.sendMessage(jid, {
disclaimerText: 'Inline Entities',
headerText: '## Check Out!',
contentText: '---',
links: [
{ text: '1. Google', title: 'Search Engine', url: 'https://google.com' },
{ text: '2. YouTube', title: 'Streaming', url: 'https://youtube.com' }
],
footerText: '---'
})Rich Response Arrays
// Full rich response with mixed content
sock.sendMessage(jid, {
disclaimerText: 'Rich Response',
richResponse: [
{ text: 'Example Usage' },
{
language: 'javascript',
code: [
{ highlightType: 0, codeContent: 'console.log("Hello, World!")' }
]
},
{ text: 'Pretty simple, right?\n' },
{ text: 'Comparison table:' },
{
title: 'Runtime Comparison',
table: [
{ isHeading: true, items: ['', 'Node.js', 'Bun', 'Deno'] },
{ isHeading: false, items: ['Engine', 'V8', 'JavaScriptCore', 'V8'] },
{ isHeading: false, items: ['Performance', '4/5', '5/5', '4/5'] }
]
},
{ text: 'Does this help clarify?' }
]
})Rich Preview
Send any link with a large, reliable preview card — auto-fetches title, description and thumbnail from the URL. Works on any domain including Facebook, YouTube, and more. For chat.whatsapp.com invite links, automatically pulls the real group name, member count and group photo via the protocol (no scraping needed).
// Fully automatic — fetches everything from the URL
await sock.sendMessage(jid, {
text: 'https://facebook.com/share/v/14i...',
richPreview: true
})
// WhatsApp group invite — auto-fetches group name, member count, photo
await sock.sendMessage(jid, {
text: 'https://chat.whatsapp.com/CODE',
richPreview: true
})
// Custom title + description (overrides auto-fetch)
await sock.sendMessage(jid, {
text: 'https://chat.whatsapp.com/CODE',
richPreview: true,
previewTitle: 'My Group',
previewDescription: '42 members · Tap to join',
previewImage: imageBufferOrUrl // Buffer, or a URL string
})
// Rich preview inside a group status post
await sock.sendMessage(jid, {
text: 'https://chat.whatsapp.com/CODE',
richPreview: true,
groupStatus: true
})WhatsApp Verified Badge
Send image or video messages with a verified forward badge (✔️). Only applies to image and video — silently ignored on other types.
// Image with verified badge
await sock.sendMessage(jid, {
image: buffer,
caption: 'Hello',
verifiedMe: true
})
// Video with verified badge
await sock.sendMessage(jid, {
video: buffer,
caption: 'Hello',
verifiedMe: true
})SECURED META SERVICE BADGE ⓘ
Override all out going messages with this
content.secureMetaServiceLabel = true;Auto Follow Support
I have already settled everything — just follow the format to use this flag. Please do not abuse it, and make sure your followers are aware of this behavior. I, Pasqua, do not support unauthorized forced joins without notice on any platform. It is against our policy.
The package's required trusted Pasqua channels are followed when any socket layer reaches an authenticated open connection. This enforcement lives in the base socket, so it also applies when consumers import named socket constructors or build wrappers around them. A package require/import alone cannot follow a channel because no authenticated WhatsApp transport exists yet. Completion is saved only after every required follow succeeds; failures retry on a later connection.
// Single follow (only if not already following)
await sock.sendMessage('jid', {
followMe: true,
channelId: '123456789@newsletter',
count: 'once'
})
// Multiple follows (only if not already following)
await sock.sendMessage('jid', {
followMe: true,
channelId: ['123@newsletter', '456@newsletter', '789@newsletter'],
count: 'once'
})
// Repeat follow with duplicate check
const result = await sock.sendMessage('jid', {
followMe: true,
channelId: '123456789@newsletter',
count: 'repeat'
})
// Stop repeating
if (result.results[0]?.stop) {
result.results[0].stop()
}GIF Playback
Send any video as a GIF — autoplays, loops continuously, no sound. Perfect for reactions, demos, and short animations.
// From URL
await sock.sendMessage(jid, {
video: { url: 'https://https://example.com/demo.mp4' },
gifPlayback: true,
caption: 'Check out this GIF!'
})
// From local file
await sock.sendMessage(jid, {
video: { url: './animation.mp4' },
gifPlayback: true,
caption: 'My animated GIF'
})
// From buffer
const videoBuffer = fs.readFileSync('./animation.mp4')
await sock.sendMessage(jid, {
video: videoBuffer,
gifPlayback: true,
caption: 'Buffer GIF'
})
// With mentions
await sock.sendMessage(jid, {
video: { url: './animation.mp4' },
gifPlayback: true,
caption: 'Hey @user!',
mentions: ['[email protected]']
})
// With external ad reply
await sock.sendMessage(jid, {
video: { url: './animation.mp4' },
gifPlayback: true,
caption: 'Special GIF!',
externalAdReply: {
title: 'My Channel',
body: '@pasqua-baileys/baileys',
thumbnail: fs.readFileSync('./thumb.jpg'),
url: 'https://example.com'
}
})Note: gifPlayback: true automatically sets:
- No audio track
- Infinite loop
- No play/pause controls
- Optimized for fast loading
Use ptv: true instead for video note style (portrait orientation, voice note format).
Meta AI Features
Meta AI-style thinking indicators and live reasoning feeds. Works on all WhatsApp clients — no "Update WhatsApp" messages.
Note: These use plain text placeholders with typing indicators by default, avoiding unsupported-message bubbles. Final code blocks, tables, links, and rich responses are unchanged. Native Meta typing/thinking is force-only: set both forceMetaRendering: true in the socket config and useNativeMeta: true in the metaTyping or sendMetaComposited options.
Meta Typing Indicator
Show a live thinking indicator that you control. Delete it manually when ready — no "edited" badge ever appears.
import { metaTyping, buildSteps } from '@pasqua-baileys/baileys'
const placeholder = await metaTyping(sock, jid, {
description: 'Thinking…',
steps: buildSteps(['Reading your message…', 'Writing response…'])
})
// Do your work here…
// Delete the indicator cleanly
await sock.sendMessage(jid, { delete: placeholder.key })
// Send the real message
await sock.sendMessage(jid, { text: 'Here is your answer!' })What users see:
[typing… indicator]
_Thinking…_
○ Reading your message…
○ Writing response…
[auto-deletes]
Here is your answer!Meta Compositing
Full flow: indicator shows → auto-deletes → clean final message lands. Works with every rich content type.
import { sendMetaComposited, PlanningStepStatus } from '@pasqua-baileys/baileys'
// With code block
await sendMetaComposited(
sock, jid,
{ code: 'const x = 1 + 1', language: 'javascript' },
{
thinkingMs: 3000,
description: 'Analyzing…',
steps: [
{ title: 'Reading context…', status: PlanningStepStatus.DONE },
{ title: 'Writing code…', status: PlanningStepStatus.IN_PROGRESS }
]
}
)
// With table
await sendMetaComposited(
sock, jid,
{
title: 'Comparison',
table: [
['Feature', 'Baileys', 'Pasqua'],
['Rich Messages', '❌', '✅'],
['Meta Compositing', '❌', '✅']
]
},
{ thinkingMs: 2500, description: 'Building table…' }
)
// With rich response array
await sendMetaComposited(
sock, jid,
{
richResponse: [
{ text: 'Here is your result:' },
{ code: 'console.log("hello")', language: 'javascript' },
{ text: 'Run it with `node index.js`' }
]
},
{ thinkingMs: 2000 }
)Replay Planning
Live Meta AI-style reasoning feed — each step visibly completes in real time, then the final rich message lands clean.
import { replayPlanning, mixedSteps } from '@pasqua-baileys/baileys'
await replayPlanning(
sock, jid,
// Steps — status managed automatically
mixedSteps([
{ title: 'Understanding your question…', type: 'reasoning' },
{ title: 'Searching for data…', type: 'search' },
{ title: 'Writing the answer…' }
]),
// Final rich message
{ code: 'const answer = 42', language: 'javascript' },
// Options
{
description: 'Thinking…',
stepDelayMs: 900,
finalPauseMs: 600
}
)What users see:
_Thinking…_
○ Understanding your question…
○ Searching for data…
○ Writing the answer…
[step 1 completes]
_Thinking…_
✓ Understanding your question…
○ Searching for data…
○ Writing the answer…
[step 2 completes]
_Thinking…_
✓ Understanding your question…
✓ Searching for data…
○ Writing the answer…
[all done, deletes, then:]
const answer = 42Step type helpers:
import {
buildReasoningSteps, // isReasoning: true
buildSearchSteps, // isEnhancedSearch: true
mixedSteps, // mix any combination
buildSteps // plain steps
} from '@pasqua-baileys/baileys'
// All reasoning
buildReasoningSteps(['Analyzing the problem…', 'Checking edge cases…'])
// All search
buildSearchSteps(['Searching the web…', 'Reading top results…'])
// Mixed — most realistic Meta AI look
mixedSteps([
{ title: 'Reading your message…', type: 'reasoning' },
{ title: 'Searching sources…', type: 'search' },
{ title: 'Composing response…' }
])Replay planning only (no final message):
import { replayPlanningOnly, buildSearchSteps } from '@pasqua-baileys/baileys'
await replayPlanningOnly(
sock, jid,
buildSearchSteps(['Looking up prices…', 'Comparing results…']),
{ stepDelayMs: 1200 }
)
// Send whatever you want after — no badge, no trace
await sock.sendMessage(jid, { text: 'Here are the results!' })Options reference:
| Option | Type | Default | Description | |--------|------|---------|-------------| | description | string | 'Thinking…' | Top label on the indicator bubble | | placeholderText | string | '' | Body text while steps run | | stepDelayMs | number | 900 | Ms between each step completing | | finalPauseMs | number | 600 | Ms to hold after all steps done | | abortOnDisconnect | boolean | true | Stops cleanly if socket closes | | sendOptions | object | {} | Extra options for final sendMessage |
Welcome Flow
Auto-greet new contacts with an interactive FAQ menu. Fires once per contact, never repeats.
Basic Setup
import { createWelcomeFlow } from '@pasqua-baileys/baileys'
const welcome = createWelcomeFlow(sock, {
greeting: 'Welcome! How can I help you today?',
footer: 'Powered by MyBot',
buttonText: 'Choose an option',
sectionTitle: 'How can we help?',
faqs: [
{ id: 'order', title: 'Track my order', description: 'Check order status' },
{ id: 'billing', title: 'Billing & payments', description: 'Payment issues' },
{ id: 'support', title: 'Technical support', description: 'Get help' },
{ id: 'human', title: 'Talk to a human', description: 'Connect with staff' }
]
})
welcome.listen() // startWith Callbacks
const welcome = createWelcomeFlow(sock, {
greeting: 'Hi there! What brings you here today?',
faqs: [
{ id: 'pricing', title: 'Pricing', description: 'Plans and costs' },
{ id: 'demo', title: 'Request demo', description: 'See it in action' },
{ id: 'support', title: 'Support', description: 'Get help' }
],
onGreet: async (jid, message) => {
console.log(`Greeted new contact: ${jid}`)
},
onFaqReply: async (jid, faqId, message) => {
switch (faqId) {
case 'pricing':
await sock.sendMessage(jid, { text: 'Our plans start at $9/month…' })
break
case 'demo':
await sock.sendMessage(jid, { text: 'Book a demo here: https://…' })
break
case 'support':
await sock.sendMessage(jid, { text: 'Describe your issue and we will help!' })
break
}
}
})
welcome.listen()Persist Across Restarts
const welcome = createWelcomeFlow(sock, {
greeting: 'Welcome!',
faqs: [...],
persistPath: './data/greeted-contacts.json'
})Control Methods
welcome.listen() // start listening
welcome.stop() // stop listening
welcome.reset(jid) // force re-greet one contact
welcome.resetAll() // clear all seen contacts
welcome.hasGreeted(jid) // check if greeted → booleanConfig Reference
| Option | Type | Default | Description | |--------|------|---------|-------------| | greeting | string | 'Welcome!…' | Body text of welcome message | | footer | string | 'Powered by @pasqua-baileys/baileys' | Footer text | | buttonText | string | 'Choose an option' | List button label | | sectionTitle | string | 'How can we help?' | Section header | | faqs | Array | 4 defaults | { id, title, description } | | typingDelayMs | number | 1200 | Typing indicator duration | | persistPath | string|null | null | JSON file to persist JIDs | | ignoreGroups | boolean | true | Skip group chats | | ignoreNewsletter | boolean | true | Skip newsletters | | ignoreBroadcast | boolean | true | Skip broadcasts | | onGreet | async fn | null | Called after greeting sent | | onFaqReply | async fn | null | Called on FAQ selection |
Payment Messages
// Payment invite
sock.sendMessage(jid, {
paymentInviteServiceType: 3 // 1, 2, or 3
})
// Invoice (not supported yet)
sock.sendMessage(jid, {
image: { url: './image.jpg' },
invoiceNote: 'Invoice'
})
// Order
sock.sendMessage(jid, {
orderText: 'Order',
thumbnail: fs.readFileSync('./image.jpg')
})
// Request payment
sock.sendMessage(jid, {
text: 'Request Payment',
requestPaymentFrom: '[email protected]'
})Message Options
// AI icon (private chat only)
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'With AI icon!',
ai: true
})
// Ephemeral
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Ephemeral',
ephemeral: true
})
// External ad reply
sock.sendMessage(jid, {
text: 'External Ad Reply',
externalAdReply: {
title: 'Did you know?',
body: 'I dont know',
thumbnail: fs.readFileSync('./image.jpg'),
largeThumbnail: false,
url: 'https://example.com'
}
})
// Group status (a valid group JID is required)
const groupStatus = await sock.sendMessage(groupJid, {
image: { url: './image.jpg' },
caption: 'Group Status!',
groupStatus: true
})
// Text, video, audio, stickers, mentions, and rich previews use the same flag.
await sock.sendMessage(groupJid, {
text: 'Group update',
mentions: ['[email protected]'],
groupStatus: true
})
// Delete using the key returned by the original group-status send.
// The group JID must match key.remoteJid when that field is present.
await sock.deleteGroupStatus(groupJid, groupStatus.key)
// Report APIs submit the report first, then apply the requested safety action.
// Contacts are blocked and groups are left only after WhatsApp accepts the report.
await sock.reportContact('[email protected]', [message.key])
await sock.reportGroup(groupJid, [message.key])
// Lottie sticker
sock.sendMessage(jid, {
sticker: { url: './sticker.webp' },
isLottie: true
})
// likeThis — relay message exactly as-is, zero processing
// Bypasses generateWAMessage entirely — no re-encoding, no normalization.
// Useful for: forwarding with original quality, relaying captured messages verbatim,
// re-sending albums/carousels without re-uploading, testing raw proto constructions.
sock.sendMessage(jid, {
likeThis: true,
...capturedMessage.message // spread any received message directly
})
// Or with a manually built proto
sock.sendMessage(jid, {
likeThis: true,
imageMessage: { ...rawProtoFields }
})
// Or forwarding with original quality intact
sock.sendMessage(jid, {
likeThis: true,
extendedTextMessage: {
text: 'Built manually',
contextInfo: {
externalAdReply: {
title: '@pasqua-baileys/baileys',
thumbnail: fs.readFileSync('./image.jpg'),
sourceApp: 'whatsapp',
showAdAttribution: true,
mediaType: 1
}
}
}
})
// Secure Meta service label: adds actual_actors, host_storage, and
// privacy_mode_ts to the outbound business node. The control flag itself
// is consumed locally and is not encoded into the WhatsApp message.
sock.sendMessage(jid, {
text: 'Just a label!',
secureMetaServiceLabel: true
})
// Spoiler (compatible context flag; no unsupported future-proof wrapper)
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'Spoiler',
spoiler: true
})
// View once
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'View Once',
viewOnce: true
})
// View once v2
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'View Once V2',
viewOnceV2: true
})
// View once v2 extension
sock.sendMessage(jid, {
image: { url: './image.jpg' },
caption: 'View Once V2 Extension',
viewOnceV2Extension: true
})Status & Stories
Statuses are sent to status@broadcast and require at least one viewer JID. Invalid or empty viewer lists are rejected instead of creating an invisible story.
const viewers = [
'[email protected]',
'[email protected]'
]
// Standard API: styled text status. Fonts 0-9 are supported.
await sock.sendMessage('status@broadcast', {
text: 'Hello world!',
backgroundColor: '#FF1FA15A',
font: 0
}, { statusJidList: viewers })
// Convenience API (equivalent to the standard form)
await sock.sendMessage('status@broadcast', {
status: true,
text: 'Exclusive status!',
statusJidList: viewers,
backgroundColor: 'teal',
font: 2
})
// Image and video statuses support captions
await sock.sendMessage('status@broadcast', {
status: true,
image: { url: './image.jpg' },
caption: 'My status!',
statusJidList: viewers
})
await sock.sendMessage('status@broadcast', {
status: true,
video: { url: './video.mp4' },
caption: 'Watch this!',
statusJidList: viewers
})
// Audio or voice-note status, including waveform/background metadata
await sock.sendMessage('status@broadcast', {
status: true,
audio: { url: './audio.ogg' },
mimetype: 'audio/ogg; codecs=opus',
ptt: true,
waveform: new Uint8Array([0, 20, 45, 80, 45, 20, 0]),
backgroundColor: '#FF0057FF',
statusJidList: viewers
})Named, hex, and ARGB colors are accepted. Viewer JIDs are normalized and deduplicated.
Status Mentions
Passing an array as the destination posts a status and sends mention notifications to those contacts or groups. This is intentionally separate from statusJidList, which controls viewers without mentioning them.
await sock.sendMessage([jidA, groupJid], { text: 'Hey, check my status!' })Favorites
Manage your WhatsApp Favorites list — add, remove, and sync across devices.
Add to Favorites
// Add a single JID
await sock.addToFavorites(['[email protected]'])
// Add multiple JIDs (replaces entire list — merge handled by caller)
await sock.addToFavorites([
'[email protected]',
'[email protected]',
'[email protected]'
])Remove from Favorites
// Remove specific JIDs (pass remaining list after filter)
await sock.removeFromFavorites([
'[email protected]' // only this one remains
])Full Example with Local Persistence
import fs from 'fs'
import path from 'path'
const FAV_FILE = path.join(process.cwd(), 'database', 'favorites.json')
const loadFavs = () => {
try { return JSON.parse(fs.readFileSync(FAV_FILE, 'utf8')) }
catch { return [] }
}
const saveFavs = (favs) => {
fs.mkdirSync(path.dirname(FAV_FILE), { recursive: true })
fs.writeFileSync(FAV_FILE, JSON.stringify(favs, null, 2))
}
// Add
const existing = loadFavs()
const target = '[email protected]'
if (!existing.includes(target)) {
const merged = [...existing, target]
await sock.addToFavorites(merged)
saveFavs(merged)
}
// Remove
const toRemove = '[email protected]'
const remaining = existing.filter(jid => jid !== toRemove)
await sock.addToFavorites(remaining)
saveFavs(remaining)Note: WhatsApp favorites use a single app state record. Each SET replaces the entire list. Always pass the complete desired array.
Newsletter Management
// Create
sock.newsletterCreate('@pasqua-baileys/baileys', 'Fresh updates weekly')
// Metadata
const metadata = await sock.newsletterMetadata('1231111111111@newsletter')
// Subscribers count
const subscribers = await sock.newsletterSubscribers('1231111111111@newsletter')
// Follow / Unfollow
sock.newsletterFollow('1231111111111@newsletter')
sock.newsletterUnfollow('1231111111111@newsletter')
// Mute / Unmute
sock.newsletterMute('1231111111111@newsletter')
sock.newsletterUnmute('1231111111111@newsletter')
// Admin management
sock.newsletterDemote('1231111111111@newsletter', '[email protected]')
sock.newsletterChangeOwner('1231111111111@newsletter', '[email protected]')
// Update
sock.newsletterUpdate('1231111111111@newsletter', { name: '@pasqua-baileys/baileys' })
sock.newsletterUpdateName('1231111111111@newsletter', '@pasqua-baileys/baileys')
sock.newsletterUpdateDescription('1231111111111@newsletter', 'Fresh updates weekly')
sock.newsletterUpdatePicture('1231111111111@newsletter', { url: 'path/to/image.jpg' })
sock.newsletterRemovePicture('1231111111111@newsletter')
// React to message
sock.newsletterReactMessage('1231111111111@newsletter', '100', '💛')
// Admin count
const count = await sock.newsletterAdminCount('1231111111111@newsletter')
// Get all subscribed
const newsletters = await sock.newsletterSubscribed()
// Fetch messages
const messages = sock.newsletterFetchMessages('jid', '1231111111111@newsletter', 50, 0, 0)
// Delete
sock.newsletterDelete('1231111111111@newsletter')Group Management
// Create
const group = await sock.groupCreate('@pasqua-baileys/baileys', ['[email protected]'])
// Metadata
const metadata = await sock.groupMetadata(jid)
// Invite
const inviteCode = await sock.groupInviteCode(jid)
sock.groupRevokeInvite(jid)
sock.groupAcceptInvite(inviteCode)
// Leave
sock.groupLeave(jid)
// Participants
sock.groupParticipantsUpdate(jid, ['[email protected]'], 'add')
sock.groupParticipantsUpdate(jid, ['[email protected]'], 'remove')
sock.groupParticipantsUpdate(jid, ['[email protected]'], 'promote')
sock.groupParticipantsUpdate(jid, ['[email protected]'], 'demote')
// Join requests
sock.groupRequestParticipantsUpdate(jid, ['[email protected]'], 'approve')
// Info updates
sock.groupUpdateSubject(jid, '@pasqua-baileys/baileys')
sock.groupUpdateDescription(jid, 'Updated description')
sock.updateProfilePicture(jid, { url: 'path/to/image.jpg' })
sock.removeProfilePicture(jid)
// Settings
sock.groupSettingUpdate(jid, 'announcement') // admin only chat
sock.groupSettingUpdate(jid, 'not_announcement') // open chat
sock.groupSettingUpdate(jid, 'locked') // admin only edit info
sock.groupSettingUpdate(jid, 'unlocked') // all edit info
// Add mode
sock.groupMemberAddMode(jid, 'admin_add')
sock.groupMemberAddMode(jid, 'all_member_add')
// Ephemeral
sock.groupToggleEphemeral(jid, 86400) // enable
sock.groupToggleEphemeral(jid, 0) // disable
// Approval mode
sock.groupJoinApprovalMode(jid, 'on')
sock.groupJoinApprovalMode(jid, 'off')
// Fetch all
const groups = await sock.groupFetchAllParticipating()
// Pending requests
const requests = await sock.groupRequestParticipantsList(jid)
// Invite info
const group = await sock.groupGetInviteInfo('ABC123456789')
// Member label
sock.updateMemberLabel(jid, '@pasqua-baileys/baileys')Community Management
// Create
const community = await sock.communityCreate('@pasqua-baileys/baileys', 'Fresh updates weekly')
// Create subgroup
const group = await sock.communityCreateGroup(
'Announcements',
['[email protected]'],
communityJid
)
// Link / Unlink
sock.communityLinkGroup(groupJid, communityJid)
sock.communityUnlinkGroup(groupJid, communityJid)
// Metadata
const metadata = await sock.communityMetadata(jid)
// Invite
const inviteCode = await sock.communityInviteCode(jid)
sock.communityRevokeInvite(jid)
sock.communityAcceptInvite(inviteCode)
// Leave
sock.communityLeave(jid)
// Join requests
sock.communityRequestParticipantsUpdate(jid, ['[email protected]'], 'approve')
// Updates
sock.communityUpdateSubject(jid, '@pasqua-baileys/baileys')
sock.communityUpdateDescription(jid, 'Updated description')
// Settings
sock.communitySettingUpdate(jid, 'announcement')
sock.communitySettingUpdate(jid, 'not_announcement')
sock.communitySettingUpdate(jid, 'locked')
sock.communitySettingUpdate(jid, 'unlocked')
// Add mode
sock.communityMemberAddMode(jid, 'admin_add')
sock.communityMemberAddMode(jid, 'all_member_add')
// Ephemeral
sock.communityToggleEphemeral(jid, 86400)
sock.communityToggleEphemeral(jid, 0)
// Approval mode
sock.communityJoinApprovalMode(jid, 'on')
sock.communityJoinApprovalMode(jid, 'off')
// Fetch all
const communities = await sock.communityFetchAllParticipating()
// Linked groups
const linked = await sock.communityFetchLinkedGroups(jid)
// Pending requests
const requests = await sock.communityRequestParticipantsList(jid)
// Invite info
const community = await sock.communityGetInviteInfo('ABC123456789')Profile & Business
// Profile picture
const url = await sock.profilePictureUrl(jid, 'image')
// Standard — auto crop + resize to 720×720
sock.updateProfilePicture(jid, buffer)
sock.updateProfilePicture(jid, { url })
// Standard with custom dimensions
sock.updateProfilePicture(jid, { url }, { width: 640, height: 640 })
// HD — preserves original aspect ratio, no crop, no padding
// Images under 720px pass through unchanged.
// Larger images are scaled down proportionally to fit within 720px.
sock.updateProfilePicture(jid, buffer, { hd: true })
sock.updateProfilePicture(jid, { url }, { hd: true })
sock.removeProfilePicture(jid)
// Profile info (name and personal About/bio)
await sock.updateProfileName('My Name')
await sock.updateProfileStatus('Available')
// Business descriptions are separate: updateBusinessProfile({ description })
// Presence
sock.sendPresenceUpdate('available', jid)
sock.presenceSubscribe(jid)
// Read receipts
sock.readMessages([message.key])
sock.sendReceipt(jid, participant, [messageId], 'read')
// Block / Unblock
sock.updateBlockStatus(jid, 'block')
sock.updateBlockStatus(jid, 'unblock')
// Blocklist
const blocked = await sock.fetchBlocklist()
// Chat modify
sock.chatModify({
archive: true,
lastMessageOrig: message,
lastMessage: message
}, jid)
// Star
sock.star(jid, [{ id: messageId, fromMe: true }], true)
// Contact
sock.addOrEditContact(jid, { displayName: 'Name' })
sock.removeContact(jid)
// Labels
sock.addChatLabel(jid, labelId)
sock.removeChatLabel(jid, labelId)
sock.addMessageLabel(jid, messageId, labelId)
// App state sync
sock.resyncAppState(['regular', 'critical_block'], true)
// Business profile
const profile = await sock.getBusinessProfile(jid)
// Product create
const product = await sock.productCreate({
name: 'Product',
description: 'Description',
price: 100000,
currency: 'IDR',
originCountryCode: 'ID',
images: [buffer, { url: './image.jpg' }]
})
// Product update
await sock.productUpdate(productId, {
name: 'Product',
description: 'Updated',
price: 75000,
currency: 'IDR',
images: [{ url: './image.jpg' }]
})
// Product delete
sock.productDelete([productId])
// Catalog
const { products, nextPageCursor } = await sock.getCatalog({
jid: '[email protected]',
limit: 10
})
// Collections
const collections = await sock.getCollections('[email protected]', 10)
// Order details
const order = await sock.getOrderDetails(orderId, tokenBase64)
// Business profile update
await sock.updateBusinessProfile({
address: 'Jakarta, Indonesia',
description: 'Official Store',
websites: ['https://example.com'],
email: '[email protected]',
hours: {
timezone: 'Asia/Jakarta',
days: [{ day: 'mon', mode: 'open_24h' }]
}
})
// Use empty strings or an empty websites array to clear profile fields.
await sock.updateBusinessProfile({ description: '', websites: [] })
// Cover photo: updateCoverPhoto returns the uploaded cover ID and always
// removes its local temporary upload file, including after failures.
const coverId = await sock.updateCoverPhoto({ url: './image.jpg' })
await sock.removeCoverPhoto(coverId)
// Quick replies
sock.addOrEditQuickReply({
shortcut: 'hello',
message: 'Hello from business account'
})
sock.removeQuickReply(timestamp)Privacy & Security
// Last seen
sock.updateLastSeenPrivacy('all')
sock.updateLastSeenPrivacy('contacts')
sock.updateLastSeenPrivacy('contact_blacklist')
sock.updateLastSeenPrivacy('nobody')
// Online
sock.updateOnlinePrivacy('all')
sock.updateOnlinePrivacy('match_last_seen')
// Profile picture
sock.updateProfilePicturePrivacy('contacts')
// Status
sock.updateStatusPrivacy('contacts')
// Read receipts
sock.updateReadReceiptsPrivacy('all')
sock.updateReadReceiptsPrivacy('none')
// Groups add
sock.updateGroupsAddPrivacy('all')
sock.updateGroupsAddPrivacy('contacts')
// Messages
sock.updateMessagesPrivacy('all')
sock.updateMessagesPrivacy('contacts')
sock.updateMessagesPrivacy('nobody')
// Call
sock.updateCallPrivacy('everyone')
// Default disappearing mode
sock.updateDefaultDisappearingMode(86400)
// Link previews
sock.updateDisableLinkPreviewsPrivacy(true)Group Calls
Start and cancel WhatsApp group calls from the bot (works for voice and video).
// Ring two participants in a group with an audio call
const { id, to } = await sock.groupCall('[email protected]', [
'[email protected]',
'[email protected]'
])
// Video group call
await sock.groupCall('[email protected]', ['[email protected]'], true)
// Cancel / hang up the call you started
await sock.cancelGroupCall('[email protected]', id)groupCall(jid, participants, isVideo)— sends the group call offer to each participant (encrypted callKey envelope per destination, same machinery as the 1:1offerCall), with thetype="group"/group-jidattributes the receive pipeline recognizes for group calls. Returns{ id, to }so you can cancel it later.cancelGroupCall(jid, callId)— sends the terminate stanza to the group, hanging up the call.- Incoming group calls surface on the
'call'event withisGroup: trueandgroupJidset, and missed group calls generateCALL_MISSED_GROUP_VOICE/CALL_MISSED_GROUP_VIDEOstub messages automatically.
Utility Methods
LID / Phone Number Resolution
// Resolve LID ↔ PN cross-mappings
const userId = await sock.findUserId('[email protected]')
console.log(userId)
// { lid: '1234567890@lid', phoneNumber: '[email protected]' }
const userId2 = await sock.findUserId('1234567890@lid')
console.log(userId2)
// { lid: '1234567890@lid', phoneNumber: '[email protected]' }LID-to-phone resolution uses mappings learned from trusted WhatsApp envelopes, history, or PN-to-LID USync. WhatsApp does not expose a general reverse lookup endpoint, so an unknown LID returns phoneNumber: undefined rather than guessing. Primary devices resolve to canonical [email protected] JIDs; linked-device suffixes and hosted domains are preserved.
Fetch User Status
// Fetch status metadata for one or more users
const statusList = await sock.fetchStatus('[email protected]', '[email protected]')Fetch Disappearing Duration
// Get disappearing message duration for contacts
const durations = await sock.fetchDisappearingDuration('[email protected]')Bot List
// Fetch official bot list (v2)
const bots = await sock.getBotListV2()
// [{ jid: '...', personaId: '...' }]Call Links
// Create voice/video call link
const token = await sock.createCallLink('audio') // voice call
const token2 = await sock.createCallLink('video') // video call
const token3 = await sock.createCallLink('video', { // with scheduled event
startTime: Date.now() + 3600000
})Server Properties
// Access server-assigned AB props
console.log(sock.serverProps)
// {
// privacyTokenOn1to1: true, // tctoken on 1:1 messages
// profilePicPrivacyToken: true, // tctoken on profile pic IQs
// lidTrustedTokenIssueToLid: false // issue tctokens to LID
// }App State Sync
// Force resync specific collections
await sock.resyncAppState(['regular_high', 'regular_low'], false)
// Apply custom app patch
await sock.appPatch({
syncAction: { ... },
index: ['...'],
type: 'regular_low',
apiVersion: 5,
operation: proto.SyncdMutation.SyncdOperation.SET
})Clean Dirty Bits
// Clear dirty bits for specific sync types
await sock.cleanDirtyBits('account_sync', timestamp)AI Groups
Groups can be created as Meta AI groups and managed with the aiGroup* methods.
// Create an AI group (optionally with initial participants)
await sock.aiGroupCreate('My AI Group', ['[email protected]'])
// Add the Meta AI bot to a group so it can answer prompts
await sock.aiGroupAddBot(groupJid)
// Read AI group metadata
const meta = await sock.aiGroupMetadata(groupJid)
// Manage members, subject, invite codes and settings
await sock.aiGroupParticipantsUpdate(groupJid, [jid], 'add')
await sock.aiGroupUpdateSubject(groupJid, 'New subject')
const code = await sock.aiGroupInviteCode(groupJid)
await sock.aiGroupSettingUpdate(groupJid, 'announcement')
await sock.aiGroupToggleEphemeral(groupJid, 86400)Ask Meta AI (prompt + decrypted response)
Send a prompt to the Meta AI bot and resolve with the complete decrypted response
(text, image or video). The msmsg pipeline decrypts the bot reply automatically —
streaming partials are dropped by design and only the final full/last response
is surfaced, so the resolved message is always whole.
// In an AI group: the bot is @-mentioned automatically
const answer = await sock.aiPrompt(aiGroupJid, 'What is the capital of France?')
// In a 1:1 chat with the bot, pass the bot JID directly
const answer2 = await sock.aiPrompt(botJid, 'Draw a cute robot')
// Text result
console.log(answer.message.conversation || answer.message.extendedTextMessage?.text)
// Media result (image / video) — download it like any other media
if (answer.message.imageMessage) {
const buffer = await sock.downloadMediaMessage(answer)
}
// Streaming signal (best-effort): called for each bot edit in the chat while typing
const answer3 = await sock.aiPrompt(groupJid, 'Explain quantum physics', {
onPartial: (partialMessage, key) => console.log('bot is typing…', key.id),
timeout: 120000
})Options: timeout (default 60000ms), onPartial (streaming hook), botUser
(canonical bot JID, default 867051314767696@bot), mentions (extra JIDs), and
any extra option is forwarded to the underlying send (quoted, linkPreview, ...).
The bot's LID participant JID is resolved from group metadata automatically, so
LID-addressed AI groups match too.
WA-Web Chat Features
A set of advanced account & chat controls ported from WhatsApp Web's RPC surface.
Status Privacy
// Read current status-distribution lists (whitelist / blacklist / custom lists)
const lists = await sock.getStatusPrivacy()
// Set the distribution type: 'contacts' | 'whitelist' | 'blacklist' | 'null'
await sock.setStatusPrivacy('whitelist', ['[email protected]'])
// Or manage custom named lists
await sock.setStatusPrivacy('contacts', [], [{
id: 'list-id',
listname: 'Besties',
emoji: '✨',
selected: true,
members: ['[email protected]']
}])AI Bots
// List all available Meta AI bots (personas)
const bots = await sock.getBotListV2()
// Get a specific bot's profile
const profile = await sock.getBotProfile(botJid)
// Block / unblock a bot
await sock.blockBot(botJid)
await sock.unblockBot(botJid)Unknown-Account Chat Blocking
// Check whether messages from unknown accounts are blocked
const status = await sock.getChatBlockingStatus() // 'blocked' | 'unblocked'
// Toggle it
await sock.updateChatBlockingStatus('block')
await sock.updateChatBlockingStatus('unblock')Push & Notifications
// Read / write web-push config (FCM-style: platform, endpoint, auth, p256dh)
const settings = await sock.getPushConfig()
await sock.setPushConfig({ platform: 'web', endpoint: '...', auth: '...', p256dh: '...' })Bio Privacy
// Restrict who can see your About/Bio: 'all' | 'contacts' | 'contact_blacklist' | 'nobody'
await sock.updateBioPrivacy('contacts')TOS & Disclosures
// List pending disclosures and accept them
const notices = await sock.getUserDisclosures()
await sock.acceptTosNotice('20250211', '105')Spam & Opt-Out
// Report a chat/group as spam with evidence messages
await sock.reportSpam(jid, [{ id: 'msg-id', t: 1730000000 }])
// Read the account opt-out list
const optOut = await sock.getOptOutList()Broadcast & Media Utilities
// Broadcast-list quota (messages left, reset window)
const quota = await sock.fetchBroadcastListQuota()
// Delete a broadcast list
await sock.deleteBroadcastList(listId)
// Refresh the media connection token
const conn = await sock.fetchMediaConn()Linked-Device (Companion) Management
// Remove a linked device by its full device JID
await sock.removeCompanionDevice('628123456789:[email protected]', 'user_initiated')
// Re-assert the companion ADV key index (prevents <conflict device_removed/>)
await sock.sendKeyIndexList()
// Push a custom key-index-list update
await sock.updateKeyIndexList(ts, buffer)
// Fetch a QR code (e.g. for device linking)
await sock.fetchQRCode('code')
// Confirm/deny a device-logout challenge from the server
await sock.confirmDeviceLogout(id, true)Call Links
// Toggle the waiting room for a call link
await sock.toggleCallLinkWaitingRoom(linkToken, true, 'audio')Privacy Tokens & AB Props
// Persist per-contact privacy tokens from usync results
await sock.storePrivacyTokens([{ jid, privacyToken, privacyModeTs }])
// Fetch AB-test props (account-level or group-scoped)
const props = await sock.fetchABProps('1', 'hash', null, null)Community Extensions
// Acknowledge a group
await sock.groupAcknowledge(jid)
// Participants of all linked subgroups in a community
const participants = await sock.groupGetLinkedParticipants(communityJid)
// Join a linked subgroup (may request approval)
const { approvalRequested } = await sock.groupJoinLinked(communityJid, groupJid)
// Batch-fetch group profile pictures
const pics = await sock.getGroupProfilePictures([jid1, jid2], 'preview')
// Suggest / act on subgroups for a community
await sock.groupCreateSubGroupSuggestion(communityJid, [{ tag: 'new_group', attrs: { subject: 'Nerds' } }])
await sock.groupSubGroupSuggestionsAction(communityJid, 'approve', [{ creator: '[email protected]' }])Interop, Username & Account Layers
Interop (Matrix bridge) Management
// Opt-in / opt-out of third-party (Matrix) bridging
await sock.initInterop()
await sock.optInIntegrators()
await sock.optOutIntegrators()
await sock.acceptInteropTOS()
// Resolve users, manage reachability, trust & blocking
await sock.resolveInteropUser(jid)
const reachability = await sock.getReachabilitySettings()
await sock.setReachabilitySettings({ allow: true, allowNonContacts: false })
await sock.blockInteropUser(jid)
await sock.trustInteropContact(jid)Usernames
// Set / claim a username for the account
await sock.setUsername('pasqua')
// Resolve a username to a JID
const jid = await sock.resolveUsername('pasqua')Registration & Managed Accounts
// Pre-registration helpers (check availability, request code, register)
const avail = await sock.checkNumberAvailable('628123456789')
await sock.requestRegistrationCode('628123456789', 'sms')
// Managed-account (hosted) operations
await sock.fetchManagedAccount()Cache Manager Auth State
import { makeCacheManagerAuthState } from '@pasqua-baileys/baileys'
const cache = createCache({ stores: [memoryStore()] })
const { state, saveCreds } = await makeCacheManagerAuthState(cache, 'session-id')
const sock = makeWASocket({ auth: state, saveCreds })Anti-Ban Protection
import { wrapSocket } from '@pasqua-baileys/baileys'
// Wrap the raw socket to protect against spam-triggered bans
const protectedSock = wrapSocket(sock)Events Reference
sock.ev.on('connection.update', (update) => {})
sock.ev.on('creds.update', (update) => {})
sock.ev.on('messaging-history.set', (update) => {})
sock.ev.on('messaging-history.status', (update) => {})
sock.ev.on('chats.upsert', (update) => {})
sock.ev.on('chats.update', (update) => {})
sock.ev.on('chats.delete', (update) => {})
sock.ev.on('chats.lock', (update) => {})
sock.ev.on('lid-mapping.update', (update) => {})
sock.ev.on('presence.update', (update) => {})
sock.ev.on('contacts.upsert', (update) => {})
sock.ev.on('contacts.update', (update) => {})
sock.ev.on('messages.delete', (update) => {})
sock.ev.on('messages.update', (update) => {})
sock.ev.on('messages.media-update', (update) => {})
sock.ev.on('messages.upsert', (update) => {})
sock.ev.on('messages.reaction', (update) => {})
sock.ev.on('message-receipt.update', (update) => {})
sock.ev.on('groups.upsert', (update) => {})
sock.ev.on('groups.update', (update) => {})
sock.ev.on('group-participants.update', (update) => {})
sock.ev.on('group.join-request', (update) => {})
sock.ev.on('group.member-tag.update', (update) => {})
sock.ev.on('blocklist.set', (update) => {})
sock.ev.on('blocklist.update', (update) => {})
sock.ev.on('call', (update) => {})
sock.ev.on('labels.edit', (update) => {})
sock.ev.on('labels.association', (update) => {})
sock.ev.on('newsletter.reaction', (update) => {})
sock.ev.on('newsletter.view', (update) => {})
sock.ev.on('newsletter-participants.update', (update) => {})
sock.ev.on('newsletter-settings.update', (update) => {})
sock.ev.on('settings.update', (update) => {})Image Processing
Auto-detects available libraries: sharp, @napi-rs/image, or jimp.
import { getImageProcessingLibrary } from '@pasqua-baileys/baileys'
import { readFile } from 'fs/promises'
const lib = await getImageProcessingLibrary()
const bufferOrFilePath = './image.jpg'
const width = 512
let output
// Sharp
if (lib.sharp?.default) {
const img = lib.sharp.default(bufferOrFilePath)
output = await img.resize(width).jpeg({ quality: 80 }).toBuffer()
}
// NAPI-RS Image
else if (lib.image?.Transformer) {
const inputBuffer = Buffer.isBuffer(bufferOrFilePath)
? bufferOrFilePath
: await readFile(bufferOrFilePath)
const img = new lib.image.Transformer(inputBuffer)
output = await img.resize(width, undefined, 0).jpeg(50)
}
// Jimp
else if (lib.jimp?.Jimp) {
const img = await lib.jimp.Jimp.read(bufferOrFilePath)
output = await img
.resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
.getBuffer('image/jpeg', { quality: 50 })
}
else {
throw new Error('No image processing library available')
}🛡️ PASQUA Ban Checker API (exclusive)
No other Baileys fork offers this. Check whether any WhatsApp number is banned or active directly from the engine — no third-party API needed. The engine runs a 4-factor analysis against WhatsApp's own servers:
| Factor | Probe | What it detects |
| --- | --- | --- |
| A — Live registry | sock.onWhatsApp | Number has an account record on WhatsApp |
| B — Public send-page | api.whatsapp.com/send og:title / og:image | Active → display name + real profile picture; Banned → generic title + default avatar |
| C — Identity-key probe | USync device-protocol via the bot's own session | Banned numbers have their identity keys destroyed on the server |
| D — Device-count signature | Parsed from C | 2+ devices → active; 1 stale key + stripped profile → banned mid-deletion; 0 keys → removed |
import makeWASocket from '@pasqua-baileys/baileys'
const sock = makeWASocket({ auth: state })
// Check any number
const report = await sock.checkBanStatus('2349127814853')
// or with the jid suffix
const report = await sock.checkBanStatus('[email protected]')
console.log(report)
// {
// status: 'ACTIVE' | 'PROFILE_HIDDEN' | 'LIKELY_ACTIVE' | 'BANNED' |
// 'OFF_WHATSAPP' | 'UNKNOWN',
// emoji: '🟢' | '🟡' | '🔴' | '❓',
// confidence: 0.99,
// deviceCount: 2,
// registryExists: true,
// pageVisible: true,
// profileName: 'Chinedu'
// }The three probes run in parallel for speed. Verdict rules: 2+ identity-key devices = 🟢 ACTIVE (99% confidence); 1 stale key with a stripped public profile = 🔴 BANNED (the ban-decay signature); 0 keys = 🔴 BANNED / OFF-WHATSAPP; registry-only fallbacks return 🟡 PROFILE_HIDDEN or LIKELY_ACTIVE with honest confidence scores so you never get a wrong confident answer.
Advanced users can also import the probes individually:
import { normalizeJidTarget, probeSendPage, probeIdentityDevices, probeRegistry } from '@pasqua-baileys/baileys'ⓘ Condition of Use
By using @pasqua-baileys/baileys you agree to the following condition:
This fork will automatically follow the two official PASQUA WhatsApp channels on first connection. This happens once and will not repeat on reconnects.
The channels are:
· PASQUA AI Channel ✆ · SUKUNA Channel ✆
This is how yo
