This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with WhatsApp or any of its subsidiaries. The official WhatsApp website is at whatsapp.com.
The maintainers of ye-bail do not support the use of this application to violate WhatsApp's Terms of Service. We emphasize personal responsibility for users to use fairly and responsibly.
Use wisely. Avoid spam. Do not use excessive automation.
npm install github:yemobyte/ye-bailnpm install github:yemobyte/ye-bail
yarn add github:yemobyte/ye-bailconst { default: makeWASocket } = require("ye-bail")
import makeWASocket from "ye-bail"const { default: makeWASocket, useMultiFileAuthState } = require('ye-bail')
async function connectToWhatsApp() {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_ye_bail')
const sock = makeWASocket({
auth: state,
printQRInTerminal: true,
})
sock.ev.on('creds.update', saveCreds)
sock.ev.on('connection.update', (update) => {
const { connection, lastDisconnect } = update
if (connection === 'close') {
const shouldReconnect =
lastDisconnect?.error?.output?.statusCode !== 401
if (shouldReconnect) {
connectToWhatsApp()
}
} else if (connection === 'open') {
console.log('Connected to WhatsApp')
}
})
sock.ev.on('messages.upsert', async (m) => {
const msg = m.messages[0]
console.log('New message:', msg.body)
})
}
connectToWhatsApp()Listen for various WhatsApp events:
sock.ev.on('messages.upsert', async (m) => {
console.log('New message:', m.messages)
})
sock.ev.on('messages.update', async (m) => {
console.log('Message update:', m)
})
sock.ev.on('message.delete', async (m) => {
console.log('Message deleted:', m)
})
sock.ev.on('connection.update', (update) => {
console.log('Connection update:', update)
})
sock.ev.on('creds.update', async () => {
console.log('Credentials updated')
})
sock.ev.on('presence.update', async (presence) => {
console.log('Presence update:', presence)
})
sock.ev.on('chats.set', async (chats) => {
console.log('Chats loaded:', chats)
})
sock.ev.on('groups.update', async (updates) => {
console.log('Group updates:', updates)
})const pollMessage = message.message?.pollCreationMessage
if (pollMessage) {
const votes = pollMessage.options
console.log('Poll options:', votes)
}sock.ev.on('messages.upsert', async (m) => {
const msg = m.messages[0]
if (msg.message?.eventMessage) {
const event = msg.message.eventMessage
console.log('Event:', event)
}
})On initial connection, ye-bail will emit:
connection.updatewith status changescreds.updatefor credential updateschats.setwith all stored chatscontacts.setwith all contactsgroups.upsertwith all groups
Create a data store to persist and sync application state:
const { Boom } = require('@hapi/boom')
const NodeCache = require('node-cache')
const dataStore = new NodeCache({ stdTTL: 24 * 60 * 60 })
sock.ev.on('chats.set', ({ chats }) => {
for (const chat of chats) {
dataStore.set(`chat_${chat.id}`, chat)
}
})
sock.ev.on('messages.upsert', (m) => {
for (const msg of m.messages) {
dataStore.set(`msg_${msg.key.id}`, msg)
}
})
sock.ev.on('contacts.upsert', (contacts) => {
for (const contact of contacts) {
dataStore.set(`contact_${contact.id}`, contact)
}
})
sock.ev.on('groups.upsert', (groups) => {
for (const group of groups) {
dataStore.set(`group_${group.id}`, group)
}
})WhatsApp uses JID (Jabber ID) format for identification:
- User ID:
[email protected](phone number with country code) - Group ID:
[email protected] - Broadcast ID:
120363000000000000@broadcast - Newsletter ID:
120363000000000000@newsletter
const userId = '[email protected]'
const groupId = '[email protected]'
await sock.sendMessage(userId, { text: 'Hello' })
await sock.sendMessage(groupId, { text: 'Group message' })const jids = ['[email protected]', '[email protected]']
const results = await sock.onWhatsApp(...jids)
console.log(results)const history = await sock.fetchMessageHistory(100, oldestMsgKey, oldestMsgTimestamp)const { default: makeWASocket, Browsers } = require("ye-bail")
const sock = makeWASocket({
browser: Browsers.ubuntu('My App'),
printQRInTerminal: true
})Phone number format: 6299999999999 (no +, (), or -)
const sock = makeWASocket({
printQRInTerminal: false
})
if (!sock.authState.creds.registered) {
const code = await sock.requestPairingCode('6299999999999')
console.log('Pairing Code:', code)
}const pairingCode = await sock.requestPairingCode('6299999999999', 'YEMOBYTE')
console.log('Custom Pairing Code:', pairingCode)Phone numbers must start with a valid country code. The library validates against a list of supported country codes (MCC - Mobile Country Code).
Supported country codes include:
- Indonesia:
62 - United States:
1 - Brazil:
55 - United Kingdom:
44 - And 200+ more countries
Example with validation:
const sock = makeWASocket({
printQRInTerminal: false
})
try {
const code = await sock.requestPairingCode('6299999999999')
console.log('Pairing Code:', code)
} catch (error) {
if (error.statusCode === 400) {
console.log('Invalid phone number. Must start with country code (e.g., 62xxx for Indonesia)')
}
}Invalid phone numbers that will throw errors:
await sock.requestPairingCode('989999999999')
await sock.requestPairingCode('08999999999')
await sock.requestPairingCode('999999999')const { default: makeWASocket, Browsers } = require("ye-bail")
const sock = makeWASocket({
browser: Browsers.macOS('Desktop'),
syncFullHistory: true
})const NodeCache = require('node-cache')
const groupCache = new NodeCache({ stdTTL: 5 * 60 })
const sock = makeWASocket({
cachedGroupMetadata: async (jid) => groupCache.get(jid)
})
sock.ev.on('groups.update', async ([event]) => {
const metadata = await sock.groupMetadata(event.id)
groupCache.set(event.id, metadata)
})Use fetchMessageHistory for better message recovery and handling poll decryption:
const history = await sock.fetchMessageHistory(100, oldestMsgKey, oldestMsgTimestamp)const sock = makeWASocket({
markOnlineOnConnect: false
})const { useMultiFileAuthState } = require('ye-bail')
const { state, saveCreds } = await useMultiFileAuthState('auth_info_ye_bail')
const sock = makeWASocket({ auth: state })
sock.ev.on('creds.update', saveCreds)const { default: makeWASocket, makeCacheManagerAuthState } = require('ye-bail')
const { caching } = require('cache-manager')
const store = await caching('memory')
const { state, saveCreds } = await makeCacheManagerAuthState(store, 'auth_info_ye_bail')
const sock = makeWASocket({ auth: state, printQRInTerminal: true })
sock.ev.on('creds.update', saveCreds)await sock.sendMessage(jid, { text: 'Hello World' })await sock.sendMessage(jid, { text: 'Hello World', ai: true })await sock.sendMessage(jid, { text: 'Reply' }, { quoted: message })const mention = '[email protected]'
await sock.sendMessage(jid, {
text: `Hello @${mention.split('@')[0]}`,
mentions: [mention]
})await sock.sendMessage(jid, { forward: message })await sock.sendMessage(jid, {
location: {
degreesLatitude: -6.2088,
degreesLongitude: 106.8456,
name: 'Jakarta, Indonesia'
}
})await sock.sendMessage(jid, {
contacts: {
displayName: 'John Doe',
contacts: [
{
displayName: 'John Doe',
vcard: `BEGIN:VCARD
VERSION:3.0
FN:John Doe
TEL:+1234567890
END:VCARD`
}
]
}
})await sock.sendMessage(jid, {
react: {
text: '👍',
key: messageKey
}
})await sock.sendMessage(jid, {
pin: {
key: messageKey,
type: 1
}
})await sock.sendMessage(jid, {
keep: {
key: messageKey,
value: true
}
})await sock.sendMessage(jid, {
poll: {
name: 'What is your favorite color?',
values: ['Red', 'Blue', 'Green'],
selectableCount: 1
}
})await sock.sendMessage(jid, {
pollResult: {
name: 'Poll Question',
values: [
['Option 1', '100'],
['Option 2', '50']
]
}
})await sock.sendMessage(jid, {
call: {
isVideo: false,
callId: 'call_123'
}
})const crypto = require('crypto')
await sock.sendMessage(jid, {
event: {
name: 'Event Name',
locationName: 'Location',
startTime: Math.floor(Date.now() / 1000),
messageSecret: crypto.randomBytes(32)
}
})await sock.sendMessage(jid, {
order: {
itemCount: 2,
status: 'pending',
surface: 'CATALOG',
orderTitle: 'My Order',
message: 'Order #12345'
}
})await sock.sendMessage(jid, {
product: {
productImage: { url: 'https://example.com/product.jpg' },
productId: '123456',
title: 'Product Name',
description: 'Description',
currencyCode: 'IDR',
priceAmount1000: '100000',
retailerId: 'Retailer',
url: 'https://example.com'
},
businessOwnerJid: '[email protected]',
caption: 'Check this product'
})await sock.sendMessage(jid, {
requestPayment: {
amount: 100000,
currency: 'IDR',
from: '[email protected]',
expiry: Date.now() + 3600000,
note: 'Payment for product'
}
})await sock.sendMessage(jid, {
paymentInvite: {
type: 'PAYMENT',
expiry: Date.now() + 3600000
}
})await sock.sendMessage(jid, {
inviteAdmin: {
jid: '120363000000000000@newsletter',
inviteExpiration: Date.now() + 604800000,
subject: 'Newsletter Name',
text: 'Invite admin',
thumbnail: Buffer.from([])
}
})await sock.sendMessage(jid, {
groupInvite: {
jid: groupId,
inviteCode: inviteCode,
inviteExpiration: Date.now() + 604800000,
subject: 'Group Name',
text: 'Join this group'
}
})await sock.sendMessage(jid, {
stickerPack: {
name: 'Pack Name',
publisher: 'Publisher',
description: 'Description',
cover: Buffer.from([...]),
stickers: [
{
sticker: { url: 'https://example.com/sticker.webp' },
emojis: ['❤', '😍']
}
]
}
})await sock.sendMessage(jid, {
sharePhoneNumber: true
})await sock.sendMessage(jid, {
requestPhoneNumber: true
})await sock.sendMessage(jid, {
text: 'Which option?',
footer: 'Select one',
buttons: [
{
buttonId: 'id1',
buttonText: { displayText: 'Option 1' },
type: 1
},
{
buttonId: 'id2',
buttonText: { displayText: 'Option 2' },
type: 1
}
]
})sock.ev.on('messages.upsert', ({ messages }) => {
const msg = messages[0]
const reply = msg.message?.buttonsResponseMessage
if (!reply) return
console.log({
id: reply.selectedButtonId,
text: reply.selectedDisplayText
})
})await sock.sendMessage(jid, {
text: 'Select an option',
footer: 'Choose wisely',
title: 'Options',
buttonText: 'View',
sections: [
{
title: 'Section 1',
rows: [
{
title: 'Option 1',
description: 'Description 1',
rowId: 'option_1'
},
{
title: 'Option 2',
description: 'Description 2',
rowId: 'option_2'
}
]
}
]
})await sock.sendMessage(jid, {
text: 'Products',
title: 'Catalog',
buttonText: 'View',
footer: 'Choose a product',
businessOwnerJid: '[email protected]',
productList: [
{
title: 'Featured',
products: [
{ productId: 'product_id_1' },
{ productId: 'product_id_2' }
]
}
]
})await sock.sendMessage(jid, {
text: 'Card Message',
title: 'Title',
cards: [
{
image: { url: 'https://example.com/image.jpg' },
title: 'Card Title',
body: 'Card Body',
footer: 'Card Footer',
buttons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'Reply',
id: 'id1'
})
}
]
}
]
})await sock.sendMessage(jid, {
text: 'Template message',
footer: 'Footer',
templateButtons: [
{
index: 1,
urlButton: {
displayText: 'Visit',
url: 'https://example.com'
}
},
{
index: 2,
callButton: {
displayText: 'Call',
phoneNumber: '+628123456789'
}
},
{
index: 3,
quickReplyButton: {
displayText: 'Reply',
id: 'reply_id'
}
}
]
})await sock.sendMessage(jid, {
text: 'Message body',
footer: 'Footer',
interactiveButtons: [
{
name: 'quick_reply',
buttonParamsJson: {
display_text: 'Quick Reply',
id: 'reply_id'
}
},
{
name: 'cta_url',
buttonParamsJson: {
display_text: 'Open Link',
url: 'https://example.com'
}
}
]
})await sock.sendMessage(jid, {
text: 'Payment via PIX',
interactiveButtons: [
{
name: 'pix',
buttonParamsJson: {
display_text: 'Pay with PIX',
pix_key: 'your_pix_key'
}
}
]
})await sock.sendMessage(jid, {
text: 'Secure Payment',
interactiveButtons: [
{
name: 'payment',
buttonParamsJson: {
display_text: 'Pay Now',
currency: 'USD',
amount: '100'
}
}
]
})await sock.sendStatusMentions(
{ text: 'Check my status', font: 0, backgroundColor: '#111111' },
['[email protected]']
)const { proto } = require('ye-bail')
await sock.sendMessage(jid, {
text: 'Open my shop',
id: 'shop_1',
shop: proto.Message.InteractiveMessage.ShopMessage.Surface.WA
})await sock.sendMessage(jid, {
text: 'See collection',
collection: {
bizJid: '[email protected]',
id: 'collection_1',
version: 1
}
})await sock.sendMessage(jid, {
text: 'AI generated message',
ai: true
})await sock.sendMessage(jid, {
text: 'Check this link: https://example.com',
linkPreview: true
})await sock.sendMessage(jid, {
video: { url: './video.gif' },
caption: 'GIF message',
gifPlayback: true
})await sock.sendMessage(jid, {
video: { url: './video.mp4' },
caption: 'Video'
})await sock.sendMessage(jid, {
audio: { url: './audio.mp3' },
mimetype: 'audio/mp4',
ptt: false
})await sock.sendMessage(jid, {
image: { url: './image.png' },
caption: 'Photo'
})await sock.sendMessage(jid, {
album: [
{ image: { url: 'https://example.com/image1.jpg' }, caption: 'Image 1' },
{ image: { url: 'https://example.com/image2.jpg' }, caption: 'Image 2' }
]
})await sock.sendMessage(jid, {
ptv: true,
video: { url: './video.mp4' },
caption: 'PTV Video'
})await sock.sendMessage(jid, {
viewOnce: true,
image: { url: 'https://example.com/image.jpg' },
caption: 'Disappears after viewing'
})const { makeNewsletterSocket, useMultiFileAuthState } = require('ye-bail')
async function connect() {
const { state, saveCreds } = await useMultiFileAuthState('auth')
const sock = makeNewsletterSocket({
auth: state,
printQRInTerminal: true
})
sock.ev.on('creds.update', saveCreds)
}const jid = '120363423175289826@newsletter'
await sock.newsletterFollow(jid)
await sock.newsletterUnfollow(jid)
await sock.newsletterMute(jid)
await sock.newsletterUnmute(jid)
const metadata = await sock.newsletterMetadata('INVITE', '0029Vb7MpjO9RZAXcgJe0n0W')
const messages = await sock.newsletterFetchMessages('jid', jid, 10, 100)
const updates = await sock.newsletterFetchUpdates(jid, 10, 100, 0)
await sock.newsletterCreate('Newsletter', 'Description', 'ALL')
await sock.newsletterUpdateName(jid, 'New Name')
await sock.newsletterUpdatePicture(jid, { url: './image.jpg' })
await sock.newsletterPromote(jid, 'user_lid')
await sock.newsletterDemote(jid, 'user_lid')
await sock.newsletterReactMessage(jid, 'server_id', '👍')
await sock.newsletterDelete(jid)await sock.sendMessage(jid, {
delete: messageKey
})await sock.sendMessage(jid, {
edit: messageKey,
text: 'Edited message text'
})const fs = require('fs')
const jimp = require('jimp')
const image = await jimp.read('./image.jpg')
const thumbnail = await image.resize(100, 100).getBuffer('image/jpeg')
await sock.sendMessage(jid, {
image: { url: './image.jpg' },
jpegThumbnail: thumbnail,
caption: 'Photo with thumbnail'
})const { downloadContentFromMessage } = require('ye-bail')
const mediaMessage = message.message?.imageMessage
if (mediaMessage) {
const stream = await downloadContentFromMessage(mediaMessage, 'image')
const chunks = []
stream.on('data', chunk => chunks.push(chunk))
stream.on('end', () => {
const buffer = Buffer.concat(chunks)
require('fs').writeFileSync('./downloaded.jpg', buffer)
})
}const { generateWAMessageContent } = require('ye-bail')
const mediaContent = await generateWAMessageContent({
image: { url: './image.jpg' }
}, {
upload: sock.waUploadToServer
})
await sock.sendMessage(jid, mediaContent)await sock.readMessages([messageKey])await sock.sendPresenceUpdate('available', jid)
await sock.sendPresenceUpdate('unavailable', jid)
await sock.sendPresenceUpdate('typing', jid)await sock.chatModify({
archive: true,
noChange: false
}, jid)await sock.chatModify({
mute: 8 * 60 * 60 * 1000
}, jid)
await sock.chatModify({
mute: null
}, jid)await sock.chatModify({
markRead: true
}, jid)
await sock.chatModify({
markRead: false
}, jid)await sock.chatModify({
deleteMediaMessage: messageKey
}, jid)await sock.chatModify({
delete: true
}, jid)await sock.chatModify({
star: messageKey
}, jid)
await sock.chatModify({
unstar: messageKey
}, jid)await sock.groupToggleEphemeral(groupJid, 24 * 60 * 60)await sock.chatModify({
clearChat: true
}, jid)const jids = ['[email protected]', '[email protected]']
const onWhatsApp = await sock.onWhatsApp(...jids)
console.log(onWhatsApp)const statuses = await sock.fetchStatus('[email protected]')
console.log(statuses)const pic = await sock.profilePictureUrl(jid)
console.log(pic)const businessProfile = await sock.getBusinessProfile(jid)
console.log(businessProfile)await sock.presenceSubscribe(jid)
sock.ev.on('presence.update', (presence) => {
console.log(presence)
})await sock.updateProfileStatus('I am online now!')await sock.updateProfileName('New Name')await sock.updateProfilePicture(jid, { url: './image.jpg' })await sock.removeProfilePicture(jid)const group = await sock.groupCreate('Group Name', [
'[email protected]',
'[email protected]'
])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')await sock.groupUpdateSubject(groupJid, 'New Group Name')await sock.groupUpdateDescription(groupJid, 'Group Description')await sock.groupSettingUpdate(groupJid, 'announcement')
await sock.groupSettingUpdate(groupJid, 'not_announcement')
await sock.groupSettingUpdate(groupJid, 'locked')
await sock.groupSettingUpdate(groupJid, 'unlocked')await sock.groupLeave(groupJid)const code = await sock.groupInviteCode(groupJid)
console.log(code)await sock.groupRevokeInvite(groupJid)const groupId = await sock.groupAcceptInvite('INVITE_CODE')const groupInfo = await sock.groupGetInviteInfo('INVITE_CODE')const metadata = await sock.groupMetadata(groupJid)
console.log(metadata.subject)
console.log(metadata.participants)
console.log(metadata.desc)sock.ev.on('messages.upsert', async (m) => {
const msg = m.messages[0]
if (msg.message?.groupInviteMessage) {
const inviteMessage = msg.message.groupInviteMessage
const groupId = await sock.groupAcceptInvite(inviteMessage.inviteCode)
}
})const requestList = await sock.groupRequestParticipantsList(groupJid)await sock.groupRequestParticipantsUpdate(groupJid, ['[email protected]'], 'approve')
await sock.groupRequestParticipantsUpdate(groupJid, ['[email protected]'], 'reject')const allGroups = await sock.groupFetchAllParticipating()await sock.groupToggleEphemeral(groupJid, 24 * 60 * 60)await sock.groupMemberAddMode(groupJid, 'all')
await sock.groupMemberAddMode(groupJid, 'admin_add')await sock.updateBlockStatus(jid, 'block')
await sock.updateBlockStatus(jid, 'unblock')const settings = await sock.fetchPrivacySettings(true)
console.log(settings)const blocklist = await sock.fetchBlocklist()await sock.updateLastSeenPrivacy('all')
await sock.updateLastSeenPrivacy('contacts')
await sock.updateLastSeenPrivacy('contact_blacklist')
await sock.updateLastSeenPrivacy('none')await sock.updateOnlinePrivacy('all')await sock.updateProfilePicturePrivacy('all')await sock.updateStatusPrivacy('all')await sock.updateReadReceiptsPrivacy('all')await sock.updateGroupsAddPrivacy('all')const modes = {
remove: 0,
'24h': 86400,
'7d': 604800,
'90d': 7776000
}
await sock.updateDefaultDisappearingMode(modes['24h'])const broadcastList = [
'[email protected]',
'[email protected]'
]
await sock.sendMessage('status@broadcast', {
text: 'This is a status'
})
for (const jid of broadcastList) {
await sock.sendMessage(jid, {
text: 'Broadcast message'
})
}sock.ev.on('chats.upsert', (chats) => {
for (const chat of chats) {
if (chat.id.includes('broadcast')) {
console.log('Broadcast:', chat)
}
}
})const { default: makeWASocket, logger: P } = require('ye-bail')
const sock = makeWASocket({
logger: P({ level: 'debug' })
})WhatsApp uses WebSocket to communicate with clients through a binary protocol:
- Connection: Client connects to WhatsApp servers via WebSocket
- Authentication: Client authenticates using stored credentials or QR code
- Frame Format: Messages are sent as binary frames with specific structure
- Events: Server sends events for messages, presence, and other updates
sock.ws.on('CB:message', (node) => {
console.log('Message node:', node)
})
sock.ws.on('CB:notification', (node) => {
console.log('Notification node:', node)
})
sock.ws.on('CB:presence', (node) => {
console.log('Presence node:', node)
})
sock.ws.on('CB:ack', (node) => {
console.log('Acknowledgement node:', node)
})Distributed under the GPL-3.0 License. See LICENSE for more information.