A professionally enhanced, feature-rich fork of the Baileys WhatsApp Web API. Built for developers who need robust, stable WhatsApp automation with LID identity mapping, AI group support, interoperability, extended message types, and improved connection handling.
Maintainer: π±π‘_ππ₯π’π§ππ¨π§ β
Important
WhatsApp has rolled out Linked Identity (LID) JIDs as part of its cross-platform interoperability initiative. Group messages and status updates now arrive with @lid domain JIDs instead of standard @s.whatsapp.net phone-number JIDs. Without LID resolution, you cannot identify who sent a message in many groups.
This fork ships a complete LIDMappingStore (bidirectional LRU cache + persistent key store) and UsyncLIDProtocol so your bot always knows the real phone number behind every @lid JID. See the π LID Mapping System section for full integration details.
- π What's New
- β¨ Features
- π¦ Installation
- π Quick Start
- π Connection & Configuration
- πΎ Authentication State Management
- π LID Mapping System
- π€ AI Groups
- π Interoperability API
- π USync Protocol
- π€ Sending Messages
- π Chat & Message Management
- π₯ Group Management
- π€ User & Profile Management
- π‘ Newsletter / Channel Management
- π Privacy & Block Management
- ποΈ Data Store Implementation
- π οΈ Utility Functions
- π‘ Best Practices & Tips
β οΈ Important Legal Notice- π Getting Help
- π License
Compared to upstream Baileys, this fork adds:
| Feature | Description |
|---|---|
| π LID Mapping System | Full LIDMappingStore with LRU cache, persistent key store, bidirectional lookups, and UsyncLIDProtocol |
| π€ AI Groups | makeAIGroupsSocket β create, manage, and receive events for WhatsApp AI-powered groups |
| π Interoperability API | makeInteropSocket β fetch third-party integrators, accept Interop TOS, opt in/out |
| π‘ USync Protocol Layer | Full WAUSync module: LID, Contact, Device, Disappearing Mode, Status, Username, Bot Profile protocols |
| π MEX / GraphQL Queries | executeWMexQuery for structured WhatsApp server queries via the w:mex IQ namespace |
ποΈ me.lid Credential |
Bot's own LID identity stored in credentials on pairing via configureSuccessfulPairing |
π lidDbMigrated Login Flag |
Signals to WhatsApp to push down LID mappings on connect |
| πΊ Group Status V2 | ToxicHandler exposed on socket for rich group story/status management |
π§ SignalRepositoryWithLIDStore |
Extended signal repository type that exposes lidMapping directly on the socket |
π£ newsletterId() Helper |
Utility to extract a clean newsletter/channel ID from any JID format |
- π Modern & Fast β Latest WA version
[2,3000,1035194821], optimised pre-key upload (812 keys) - π Full LID Identity Resolution β Bidirectional LIDβPN mapping with LRU cache, USync lookup, and persistent storage
- π€ AI Groups Support β Create and manage WhatsApp's AI-powered group type
- π Cross-Platform Interop β Third-party integrator management (BirdyChat, Haiket, and more)
- π§ Enhanced Stability β Improved connection handling, rate-limit backoff, 5 s keepAlive grace
- π± Multi-Device Support β Full WhatsApp multi-device protocol with improved
historySyncConfig - π End-to-End Encryption β Signal Protocol,
inlineInitialPayloadInE2EeMsg: true - π¨ Extended Message Types β Interactive, album, event, poll result, group status, payment, product
- π₯ Advanced Group Management β Group controls, group status V2, communities support
- πΎ Flexible Auth β Multi-file auth state with
makeCacheableSignalKeyStore - π‘ Full Newsletter/Channel API β Follow, create, metadata,
newsletterId()helper - π οΈ Developer Friendly β
toxicHandlerandToxicHandlerexposed on socket, clean API - π WebSocket Improvements β
perMessageDeflate: false, 100 MB max payload
npm install toxic-baileysyarn add toxic-baileysnpm install github:xhclintohn/Baileys{
"dependencies": {
"@whiskeysockets/baileys": "npm:toxic-baileys@latest"
}
}Basic Connection (QR Code)
import makeWASocket, { useMultiFileAuthState, DisconnectReason } from 'toxic-baileys';
import { Boom } from '@hapi/boom';
async function connectToWhatsApp() {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');
const sock = makeWASocket({ auth: state, printQRInTerminal: true });
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! Bot LID:', sock.user?.lid);
}
});
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const m of messages) {
if (!m.message) continue;
console.log('Message:', JSON.stringify(m, undefined, 2));
}
});
sock.ev.on('creds.update', saveCreds);
}
connectToWhatsApp().catch(console.error);Pairing Code (no QR)
import makeWASocket, { useMultiFileAuthState } from 'toxic-baileys';
async function connectWithPairing() {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');
const sock = makeWASocket({ auth: state, printQRInTerminal: false });
sock.ev.on('creds.update', saveCreds);
if (!sock.authState.creds.registered) {
const phoneNumber = '254712345678'; // no + or spaces
const code = await sock.requestPairingCode(phoneNumber);
console.log('Pairing Code:', code);
}
}
connectWithPairing().catch(console.error);Full Socket Configuration
import makeWASocket, { Browsers, makeCacheableSignalKeyStore } from 'toxic-baileys';
import NodeCache from '@cacheable/node-cache';
const groupCache = new NodeCache({ stdTTL: 300, useClones: false });
const sock = makeWASocket({
browser: Browsers.macOS('Chrome'),
syncFullHistory: true,
markOnlineOnConnect: false,
connectTimeoutMs: 60_000,
defaultQueryTimeoutMs: 60_000,
keepAliveIntervalMs: 30_000,
generateHighQualityLinkPreview: true,
cachedGroupMetadata: async (jid) => groupCache.get(jid),
getMessage: async (key) => await yourStore.getMessage(key),
});
sock.ev.on('groups.update', async ([event]) => {
const metadata = await sock.groupMetadata(event.id);
groupCache.set(event.id, metadata);
});Multi-File Auth (Development)
import makeWASocket, { useMultiFileAuthState } from 'toxic-baileys';
const { state, saveCreds } = await useMultiFileAuthState('./auth_info');
const sock = makeWASocket({ auth: state });
sock.ev.on('creds.update', saveCreds);Custom Database Auth (Production)
import makeWASocket, { makeCacheableSignalKeyStore } from 'toxic-baileys';
const myAuthState = {
creds: await db.getAuthCreds(),
keys: makeCacheableSignalKeyStore(await db.getSignalKeys(), console)
};
const sock = makeWASocket({ auth: myAuthState });
sock.ev.on('creds.update', async (creds) => await db.saveAuthCreds(creds));Important
This is one of the most critical new features in this fork. WhatsApp is migrating groups to use Linked Identity (LID) JIDs β identifiers in the @lid domain that replace @s.whatsapp.net phone-number JIDs for privacy and cross-platform reasons. If you receive a message from 1234567890:0@lid and don't have a mapping, you cannot identify the sender.
A Linked Identity (LID) is an opaque numeric identifier assigned to each WhatsApp account in the @lid domain. WhatsApp uses LIDs in groups to decouple a person's phone number from their group identity. The LID is stable across phone number changes and is used for the Signal encryption protocol in newer group types.
Standard JID: 254712345678@s.whatsapp.net β phone number visible
LID JID: 9876543210:0@lid β opaque β phone number hidden
This fork implements LIDMappingStore in src/Signal/lid-mapping.ts:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LIDMappingStore β
β β
β LRU Cache (3-day TTL, auto-purge) β
β pn:{pnUser} β lidUser β
β lid:{lidUser} β pnUser β
β β β
β SignalKeyStoreWithTransaction β
β lid-mapping/{pnUser} β lidUser β
β lid-mapping/{lidUser}_reverse β pnUser β
β β β
β USync Lookup (UsyncLIDProtocol) β
β Query WhatsApp servers via w:sync IQ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Persistent file naming (with useMultiFileAuthState):
Session/
lid-mapping-{pnUser}.json β pnUser β lidUser
lid-mapping-{lidUser}_reverse.json β lidUser β pnUser (reverse lookup)
When your bot pairs with WhatsApp, its own LID is stored in the session credentials:
// Stored automatically by configureSuccessfulPairing()
// Access it any time after connecting:
const myLid = sock.user?.lid;
console.log('My LID:', myLid); // e.g. "9876543210@lid"The login payload includes lidDbMigrated: false. This signals to WhatsApp that the client has not yet migrated its local LID database, which causes WhatsApp to push down a full set of LIDβPN mappings on the initial connection β seeding your local store automatically.
// On the socket (via SignalRepositoryWithLIDStore):
sock.signalRepository.lidMapping.getPNForLID(lidJid) // string | null
sock.signalRepository.lidMapping.getLIDForPN(pnJid) // string | null
sock.signalRepository.lidMapping.getPNsForLIDs(lidJids[]) // LIDMapping[] | null
sock.signalRepository.lidMapping.getLIDsForPNs(pnJids[]) // LIDMapping[] | null
sock.signalRepository.lidMapping.storeLIDPNMappings(pairs[]) // void
// Type:
type LIDMapping = { lid: string; pn: string }import makeWASocket, { useMultiFileAuthState, makeCacheableSignalKeyStore } from 'toxic-baileys';
const { state, saveCreds } = await useMultiFileAuthState('./Session');
const lidPhoneCache = new Map();
const sock = makeWASocket({
auth: {
creds: state.creds,
keys: makeCacheableSignalKeyStore(state.keys, console)
}
});
sock.ev.on('creds.update', saveCreds);
// Wire up LID globals for use throughout your bot
globalThis.resolvePhoneFromLid = (lidJid) => {
// synchronous β only works if the mapping is already cached
return null;
};
globalThis.resolvePhoneFromLidAsync = async (lidJid) => {
return await sock.signalRepository.lidMapping.getPNForLID(lidJid);
};
// Keep the in-memory cache warm from live LID mapping events
sock.ev.on('lid-mapping.update', (map) => {
for (const [lid, pn] of Object.entries(map)) {
const lidNum = lid.split('@')[0].split(':')[0];
const phone = String(pn).split('@')[0].split(':')[0].replace(/\D/g, '');
if (lidNum && phone) lidPhoneCache.set(lidNum, phone);
}
});
// Resolving a sender from a group message
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const m of messages) {
const sender = m.key.participant || m.key.remoteJid || '';
if (sender.endsWith('@lid')) {
const resolved = await sock.signalRepository.lidMapping.getPNForLID(sender);
console.log(`LID ${sender} β ${resolved ?? 'unknown'}`);
}
}
});For maximum reliability, resolve LIDs in this priority order:
async function resolveLid(lidJid, sock, lidPhoneCache) {
const lidNum = lidJid.split('@')[0].split(':')[0].replace(/\D/g, '');
if (!lidNum) return null;
// 1. In-memory LRU cache (fastest)
const cached = lidPhoneCache?.get(lidNum);
if (cached) return String(cached).replace(/\D/g, '') + '@s.whatsapp.net';
// 2. Baileys LIDMappingStore (LRU + persistent key store)
const fromStore = await sock.signalRepository?.lidMapping?.getPNForLID(lidJid);
if (fromStore) {
const num = fromStore.split('@')[0].replace(/\D/g, '');
if (num) { lidPhoneCache?.set(lidNum, num); return num + '@s.whatsapp.net'; }
}
// 3. Session file (lid-mapping-{lidNum}_reverse.json)
try {
const revFile = `./Session/lid-mapping-${lidNum}_reverse.json`;
if (fs.existsSync(revFile)) {
const jid = JSON.parse(fs.readFileSync(revFile, 'utf-8'));
const num = String(jid).split('@')[0].replace(/\D/g, '');
if (num && num !== lidNum) {
lidPhoneCache?.set(lidNum, num);
return num + '@s.whatsapp.net';
}
}
} catch {}
// 4. Group metadata participant scan (network call β use sparingly)
try {
const meta = await sock.groupMetadata(chatJid);
for (const p of meta.participants || []) {
const pLid = (p.lid || p.id || '').split('@')[0].split(':')[0].replace(/\D/g, '');
if (pLid !== lidNum) continue;
const pBase = p.id || p.jid || '';
if (pBase && !pBase.endsWith('@lid')) {
const num = pBase.split('@')[0].replace(/\D/g, '');
if (num) { lidPhoneCache?.set(lidNum, num); return num + '@s.whatsapp.net'; }
}
}
} catch {}
return null; // unresolvable β pass lidJid through and let WhatsApp handle it
}Note
Session file cleanup warning: If your bot cleans up session files periodically, make sure lid-mapping-* files are preserved in your keep-list, or your mapping cache will be lost on restart and need to be re-fetched from WhatsApp's servers.
WhatsApp introduced AI-powered groups β special group types that include an AI bot participant. This fork exposes full management of these groups via makeAIGroupsSocket (which is included in the socket chain automatically).
makeWASocket
ββ makeInteropSocket
ββ makeAIGroupsSocket
ββ makeCommunitiesSocket
ββ makeGroupsSocket
ββ ...
// Automatically fires when an AI group is created
sock.ev.on('groups.upsert', ([groupMeta]) => {
console.log('New group:', groupMeta.id, groupMeta.subject);
});
// Fires on participant changes (add/remove/promote/demote) in AI groups
sock.ev.on('group-participants.update', ({ id, participants, action }) => {
console.log(`${action} in ${id}:`, participants);
});// Fetch full metadata for an AI group (uses interactive query)
const meta = await sock.aiGroupMetadata('1234567890@g.us');
console.log('AI group subject:', meta.subject);
console.log('Participants:', meta.participants);// Add an AI bot participant to an AI group
await sock.aiGroupAddBot('1234567890@g.us');WhatsApp's interoperability (interop) system allows messages to be exchanged with third-party messaging platforms. This fork exposes the full interop socket layer via makeInteropSocket.
| ID | Name | Notes |
|---|---|---|
12 |
BirdyChat | Cross-platform messaging bridge |
13 |
Haiket | Cross-platform messaging bridge |
const integrators = await sock.fetchIntegrators();
/*
[
{
id: 12,
name: 'BirdyChat',
status: 'active', // 'active' | 'onboarding' | 'removed'
identifierType: 'pn', // 'email' | 'pn' | 'username'
optedIn: false,
features: { groupMessaging: true }
},
...
]
*/// Must be called before opting in to any integrator
await sock.acceptInteropTOS();// Opt in to all default integrators (BirdyChat + Haiket)
await sock.optInIntegrators();
// Opt in to specific integrators by ID
await sock.optInIntegrators([12]);WAUSync is WhatsApp's batch user-info query system. This fork exports the full USync module so you can run structured queries against WhatsApp's w:sync:user IQ namespace.
| Protocol Class | Purpose |
|---|---|
USyncContactProtocol |
Resolve contact info |
USyncDeviceProtocol |
Fetch registered devices |
USyncDisappearingModeProtocol |
Get disappearing message settings |
USyncStatusProtocol |
Fetch user status |
USyncUsernameProtocol |
Resolve usernames |
UsyncBotProfileProtocol |
Fetch AI bot profile data |
UsyncLIDProtocol |
Resolve LID β phone-number pairs |
import { USyncQuery, USyncUser, UsyncLIDProtocol, USyncDeviceProtocol } from 'toxic-baileys';
const query = new USyncQuery();
query.withContext('interactive');
query.withProtocol(new UsyncLIDProtocol());
query.withProtocol(new USyncDeviceProtocol());
const user = new USyncUser();
user.withPhone('254712345678@s.whatsapp.net');
query.withUser(user);
const result = await sock.executeUSyncRequest(query);
console.log(result);For structured server queries, this fork exposes executeWMexQuery β a GraphQL-style query executor over WhatsApp's w:mex IQ namespace:
import { executeWMexQuery } from 'toxic-baileys';
const result = await executeWMexQuery(
{ userId: '254712345678' }, // variables
'xwa2_user_profile', // queryId (WhatsApp's internal query name)
'xwa2_user_profile', // dataPath (key in the response data object)
sock.query.bind(sock), // query function
sock.generateMessageTag.bind(sock)
);
console.log('MEX result:', result);Text, Reply, Mention, Forward, Edit, Delete, React
await sock.sendMessage(jid, { text: 'Hello World!' });
await sock.sendMessage(jid, { text: 'Reply!' }, { quoted: m });
await sock.sendMessage(jid, { text: 'Hi @user!', mentions: ['254712345678@s.whatsapp.net'] });
await sock.sendMessage(jid, { forward: m });
const sent = await sock.sendMessage(jid, { text: 'Original' });
await sock.sendMessage(jid, { text: 'Edited!', edit: sent.key });
await sock.sendMessage(jid, { delete: sent.key });
await sock.sendMessage(jid, { react: { text: 'π₯', key: m.key } });
await sock.sendMessage(jid, { react: { text: '', key: m.key } }); // remove reactionImage, Video, Audio, Document, Sticker, View Once
await sock.sendMessage(jid, { image: { url: './image.jpg' }, caption: 'Caption' });
await sock.sendMessage(jid, { image: fs.readFileSync('./img.jpg') });
await sock.sendMessage(jid, { video: { url: './video.mp4' }, caption: 'Video' });
await sock.sendMessage(jid, { video: { url: './animation.mp4' }, gifPlayback: true });
await sock.sendMessage(jid, { audio: { url: './voice.ogg' }, mimetype: 'audio/ogg; codecs=opus', ptt: true });
await sock.sendMessage(jid, { audio: { url: './song.mp3' }, mimetype: 'audio/mp4' });
await sock.sendMessage(jid, { document: { url: './file.pdf' }, fileName: 'MyDoc.pdf', mimetype: 'application/pdf' });
await sock.sendMessage(jid, { sticker: { url: './sticker.webp' } });
await sock.sendMessage(jid, { image: fs.readFileSync('./img.jpg'), viewOnce: true });Interactive Message with Native Flow Buttons
// Copy button
await sock.sendMessage(jid, {
interactiveMessage: {
header: 'toxic-baileysβ’',
title: 'Hello World',
footer: 'By π±π‘_ππ₯π’π§ππ¨π§ β',
buttons: [
{
name: 'cta_copy',
buttonParamsJson: JSON.stringify({ display_text: 'Copy Code', id: '1', copy_code: 'TOXIC123' })
}
]
}
}, { quoted: m });
// URL button
await sock.sendMessage(jid, {
interactiveMessage: {
header: 'Visit Us',
title: 'toxic-baileys',
footer: 'GitHub',
buttons: [
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({ display_text: 'Open GitHub', url: 'https://github.com/xhclintohn/Baileys' })
}
]
}
}, { quoted: m });
// With image thumbnail
await sock.sendMessage(jid, {
interactiveMessage: {
header: 'With Image',
title: 'toxic-baileysβ’',
footer: 'Best Baileys Fork',
image: { url: 'https://example.com/image.jpg' },
buttons: [
{ name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copy', id: '1', copy_code: 'HELLO' }) }
]
}
}, { quoted: m });
// Single select list
await sock.sendMessage(jid, {
interactiveMessage: {
header: 'Choose',
title: 'Menu',
footer: 'Select below',
nativeFlowMessage: {
buttons: [
{
name: 'single_select',
buttonParamsJson: JSON.stringify({
title: 'Options',
sections: [
{
title: 'Commands',
rows: [
{ title: 'Option 1', description: 'First', id: 'opt_1' },
{ title: 'Option 2', description: 'Second', id: 'opt_2' }
]
}
]
})
}
]
}
}
}, { quoted: m });
// With externalAdReply
await sock.sendMessage(jid, {
interactiveMessage: {
header: 'Premium',
title: 'toxic-baileysβ’',
footer: 'By π±π‘_ππ₯π’π§ππ¨π§',
externalAdReply: {
title: 'toxic-baileys',
body: 'Best WhatsApp Library',
mediaType: 1,
thumbnailUrl: 'https://example.com/thumb.jpg',
sourceUrl: 'https://github.com/xhclintohn/Baileys',
showAdAttribution: true,
renderLargerThumbnail: true
},
buttons: [
{ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Visit', url: 'https://github.com/xhclintohn/Baileys' }) }
]
}
}, { quoted: m });Send multiple images/videos in a single album
await sock.sendMessage(jid, {
albumMessage: [
{ image: fs.readFileSync('./photo1.jpg'), caption: 'First photo' },
{ image: { url: 'https://example.com/photo2.jpg' }, caption: 'Second photo' },
{ video: fs.readFileSync('./clip.mp4'), caption: 'A video clip' }
]
}, { quoted: m });Send WhatsApp event invitations
await sock.sendMessage(jid, {
eventMessage: {
isCanceled: false,
name: 'toxic-baileys Launch',
description: 'Join us for the launch!',
location: { degreesLatitude: -1.2921, degreesLongitude: 36.8219, name: 'Nairobi, Kenya' },
joinLink: 'https://call.whatsapp.com/video/your-link',
startTime: String(Math.floor(Date.now() / 1000) + 3600),
endTime: String(Math.floor(Date.now() / 1000) + 7200),
extraGuestsAllowed: true
}
}, { quoted: m });Polls and displaying poll results
// Create poll
await sock.sendMessage(jid, {
poll: {
name: 'Best WhatsApp library?',
values: ['toxic-baileys', 'Baileys', 'Other'],
selectableCount: 1
}
});
// Display poll results
await sock.sendMessage(jid, {
pollResultMessage: {
name: 'Best Library Results',
pollVotes: [
{ optionName: 'toxic-baileys', optionVoteCount: '42' },
{ optionName: 'Baileys', optionVoteCount: '10' },
{ optionName: 'Other', optionVoteCount: '2' }
]
}
}, { quoted: m });Post status/story to a group (Group Status V2)
await sock.sendMessage(groupJid, { groupStatusMessage: { text: 'Hello group! π' } });
await sock.sendMessage(groupJid, { groupStatusMessage: { image: fs.readFileSync('./banner.jpg'), caption: 'Update!' } });
await sock.sendMessage(groupJid, { groupStatusMessage: { video: fs.readFileSync('./promo.mp4'), caption: 'Promo' } });
await sock.sendMessage(groupJid, { groupStatusMessage: { audio: fs.readFileSync('./audio.mp4'), mimetype: 'audio/mp4' } });
// Using ToxicHandler directly (exposed on socket)
const storyResult = await sock.toxicHandler.handleGroupStory(content, groupJid, quotedMsg);Send payment requests
await sock.sendMessage(jid, {
requestPaymentMessage: {
currency: 'KES',
amount: 500000,
from: m.sender,
background: { id: 'DEFAULT', placeholderArgb: 0xFFF0F0F0 }
}
}, { quoted: m });Send product catalog messages
await sock.sendMessage(jid, {
productMessage: {
title: 'Premium Script',
description: 'Best bot script available',
thumbnail: { url: 'https://example.com/product.jpg' },
productId: 'PROD001',
retailerId: 'xhclinton',
url: 'https://github.com/xhclintohn/Baileys',
body: 'Full featured automation',
footer: 'Special price',
priceAmount1000: 10000,
currencyCode: 'USD',
buttons: [{ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Buy Now', url: 'https://github.com/xhclintohn/Baileys' }) }]
}
}, { quoted: m });Classic buttons, list messages, template buttons
// Classic buttons
await sock.sendMessage(jid, {
text: 'Choose:',
footer: 'toxic-baileysβ’',
buttons: [
{ buttonId: 'btn_1', buttonText: { displayText: 'Option 1' }, type: 1 },
{ buttonId: 'btn_2', buttonText: { displayText: 'Option 2' }, type: 1 }
],
headerType: 1
});
// List message
await sock.sendMessage(jid, {
text: 'Pick one:',
footer: 'toxic-baileysβ’',
title: 'Menu',
buttonText: 'Open List',
sections: [
{
title: 'Section 1',
rows: [
{ title: 'Item 1', rowId: 'row_1', description: 'Description 1' },
{ title: 'Item 2', rowId: 'row_2', description: 'Description 2' }
]
}
]
});Read receipts, archive, pin, delete, star
await sock.readMessages([m.key]);
await sock.sendReadReceipt(jid, participant, [m.key.id]);
await sock.chatModify({ archive: true, lastMessages: [{ key: m.key, messageTimestamp: m.messageTimestamp }] }, jid);
await sock.chatModify({ pin: true }, jid);
await sock.chatModify({ delete: true, lastMessages: [{ key: m.key, messageTimestamp: m.messageTimestamp }] }, jid);
await sock.chatModify({ star: { messages: [{ id: m.key.id, fromMe: m.key.fromMe }], star: true } }, jid);
await sock.sendMessage(jid, { delete: m.key });Create, modify, manage participants
const group = await sock.groupCreate('Group Name', ['254712345678@s.whatsapp.net']);
await sock.groupParticipantsUpdate(jid, ['254712345678@s.whatsapp.net'], 'add');
await sock.groupParticipantsUpdate(jid, ['254712345678@s.whatsapp.net'], 'remove');
await sock.groupParticipantsUpdate(jid, ['254712345678@s.whatsapp.net'], 'promote');
await sock.groupParticipantsUpdate(jid, ['254712345678@s.whatsapp.net'], 'demote');
await sock.groupUpdateSubject(jid, 'New Name');
await sock.groupUpdateDescription(jid, 'New description');
await sock.groupSettingUpdate(jid, 'announcement');
await sock.groupSettingUpdate(jid, 'not_announcement');
await sock.groupSettingUpdate(jid, 'locked');
await sock.groupSettingUpdate(jid, 'unlocked');
await sock.groupLeave(jid);
const inviteCode = await sock.groupInviteCode(jid);
const meta = await sock.groupMetadata(jid);
const all = await sock.groupFetchAllParticipating();Profile photo, status, presence
await sock.updateProfilePicture(jid, { url: './avatar.jpg' });
await sock.removeProfilePicture(jid);
const pp = await sock.profilePictureUrl(jid, 'image');
await sock.updateProfileStatus('Hey there! I am using toxic-baileysβ’');
await sock.updateProfileName('My Bot Name');
await sock.fetchStatus(jid);
const exists = await sock.onWhatsApp('254712345678@s.whatsapp.net');
await sock.sendPresenceUpdate('available', jid);
await sock.sendPresenceUpdate('composing', jid);
await sock.sendPresenceUpdate('recording', jid);
await sock.sendPresenceUpdate('paused', jid);Create, follow, manage newsletters and channels
import { newsletterId } from 'toxic-baileys';
// Follow a channel
await sock.newsletterFollow('1234567890@newsletter');
// Unfollow
await sock.newsletterUnfollow('1234567890@newsletter');
// Fetch channel metadata
const meta = await sock.newsletterMetadata('invite', 'your-invite-link');
console.log(meta.id, meta.name, meta.subscribers);
// Create a channel
const channel = await sock.newsletterCreate('Channel Name', { description: 'My channel' });
// Send a message to your channel
await sock.sendMessage('1234567890@newsletter', { text: 'Channel update!' });
// newsletterId() helper β extract clean ID from any format
const cleanId = newsletterId('1234567890@newsletter');
const cleanIdFromLink = newsletterId('https://whatsapp.com/channel/yourlink');
console.log('Clean ID:', cleanId);
// Mute/unmute
await sock.newsletterMute('1234567890@newsletter');
await sock.newsletterUnmute('1234567890@newsletter');Privacy settings and block list
await sock.updateProfilePicturePrivacy('contacts'); // 'all' | 'contacts' | 'contact_blacklist' | 'none'
await sock.updateStatusPrivacy('contacts');
await sock.updateReadReceiptsPrivacy('all'); // 'all' | 'none'
await sock.updateGroupsAddPrivacy('contacts');
await sock.updateLastSeenPrivacy('contacts');
await sock.updateOnlinePrivacy('all');
const privacy = await sock.fetchPrivacySettings(true);
await sock.updateBlockStatus(jid, 'block');
await sock.updateBlockStatus(jid, 'unblock');
const blocked = await sock.fetchBlocklist();In-Memory Store (Development)
import makeWASocket, { makeInMemoryStore } from 'toxic-baileys';
const store = makeInMemoryStore({ logger: console });
store.readFromFile('./baileys_store.json');
setInterval(() => store.writeToFile('./baileys_store.json'), 10_000);
const sock = makeWASocket({});
store.bind(sock.ev);Using ToxicHandler Directly
// ToxicHandler and toxicHandler are both exposed on the socket
const { toxicHandler } = sock;
const paymentContent = await toxicHandler.handlePayment(content, quoted);
const interactive = await toxicHandler.handleInteractive(content, jid, quoted);
const album = await toxicHandler.handleAlbum(content, jid, quoted);
const event = await toxicHandler.handleEvent(content, jid, quoted);
const pollResult = await toxicHandler.handlePollResult(content, jid, quoted);
const groupStory = await toxicHandler.handleGroupStory(content, jid, quoted);Core Utilities
import {
getContentType, areJidsSameUser, isJidGroup, isJidBroadcast,
isJidStatusBroadcast, isJidNewsLetter, jidNormalizedUser,
isLidUser, isPnUser, isHostedPnUser,
generateMessageID, generateMessageIDV2, generateWAMessage,
generateWAMessageContent, generateWAMessageFromContent,
downloadContentFromMessage, getAggregateVotesInPollMessage,
extractMessageContent, normalizeMessageContent, newsletterId,
proto
} from 'toxic-baileys';
const type = getContentType(m.message);
console.log(isJidGroup('123@g.us')); // true
console.log(isJidNewsLetter('123@newsletter')); // true
console.log(isLidUser('12345@lid')); // true
// Extract clean newsletter ID
const id = newsletterId('https://whatsapp.com/channel/mylink');
// Download media
const stream = await downloadContentFromMessage(m.message.imageMessage, 'image');
const chunks = [];
for await (const chunk of stream) chunks.push(chunk);
const buffer = Buffer.concat(chunks);
// Aggregate poll votes
const votes = getAggregateVotesInPollMessage(
{ message: pollMsg.message, pollUpdates },
sock.user.id
);- Always implement reconnect logic on
connection === 'close' - Cache group metadata using
cachedGroupMetadata - Use
markOnlineOnConnect: falseto still receive phone notifications - Set
syncFullHistory: truefor complete history
- Always preserve
lid-mapping-*files in session cleanup routines - Listen to the
lid-mapping.updateevent to keep your in-memory cache warm - Fall back to group metadata scan only when all cache/store lookups fail
- Use
sock.user?.lidto access your own bot's LID identity
- Use
@cacheable/node-cachefor group metadata caching - Implement a message queue with rate limiting for bulk sends
- Use databases instead of in-memory store for production
Auto-reconnect pattern
async function connectWithRetry(maxRetries = 10) {
let attempt = 0;
const connect = async () => {
attempt++;
try {
const { state, saveCreds } = await useMultiFileAuthState('./auth');
const sock = makeWASocket({ auth: state });
sock.ev.on('creds.update', saveCreds);
sock.ev.on('connection.update', async ({ connection, lastDisconnect }) => {
if (connection === 'close') {
const code = lastDisconnect?.error?.output?.statusCode;
if (code === DisconnectReason.loggedOut) return;
const delay = Math.min(5000 * attempt, 60000);
if (attempt < maxRetries) setTimeout(connect, delay);
} else if (connection === 'open') {
attempt = 0;
}
});
} catch (err) {
if (attempt < maxRetries) setTimeout(connect, 5000 * attempt);
}
};
await connect();
}Message queue with rate limiting
class MessageQueue {
constructor(sock, delayMs = 1000) {
this.sock = sock;
this.queue = [];
this.processing = false;
this.delayMs = delayMs;
}
async add(jid, content, options = {}) {
this.queue.push({ jid, content, options });
if (!this.processing) this._process();
}
async _process() {
this.processing = true;
while (this.queue.length > 0) {
const { jid, content, options } = this.queue.shift();
try {
await this.sock.sendMessage(jid, content, options);
await new Promise(r => setTimeout(r, this.delayMs));
} catch (err) {
console.error('Send failed:', err.message);
}
}
this.processing = false;
}
}
const queue = new MessageQueue(sock, 1500);
await queue.add(jid, { text: 'Message 1' });
await queue.add(jid, { text: 'Message 2' });This project is NOT affiliated with, authorized, maintained, sponsored, or endorsed by WhatsApp LLC or any of its affiliates.
- Only message users who have explicitly consented
- Do NOT use for spamming, bulk unsolicited messaging, or harassment
- Respect WhatsApp's Terms of Service and rate limits
- The maintainer assumes NO liability for misuse or damages
- GitHub Issues β github.com/xhclintohn/Baileys/issues
- Response Time β Typically within 24β48 hours
MIT License. See LICENSE for details.
Credits: Original Baileys by WhiskeySockets Β· toxic-baileys enhancements by π±π‘_ππ₯π’π§ππ¨π§ β
π toxic-baileysβ’ β Crafted with β€οΈ by π±π‘_ππ₯π’π§ππ¨π§ β
The most powerful WhatsApp automation toolkit
β Star the repository if this helped you!