
Company News
/Security News
Socket Selected for OpenAI's Cybersecurity Grant Program
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.
@k4la/baileys
Advanced tools
This project is not affiliated, associated, authorized, endorsed, or officially connected with WhatsApp or any of its subsidiaries. The official WhatsApp website is at whatsapp.com.
The Baileys maintainers do not support the use of this application for violating WhatsApp's Terms of Service. We emphasize users' personal responsibility to use it fairly and responsibly.
Use wisely. Avoid spam. Do not use excessive automation.
npm i @k4la/baileys
const { default: makeWASocket } = require("baileys")
// or ES6
import makeWASocket from "baileys"
const { default: makeWASocket, DisconnectReason, useMultiFileAuthState } = require('baileys')
const { Boom } = require('@hapi/boom')
async function connectToWhatsApp() {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
const sock = makeWASocket({
auth: state,
printQRInTerminal: true,
browser: ['Baileys', 'Desktop', '3.0']
})
sock.ev.on('connection.update', (update) => {
const { connection, lastDisconnect } = update
if(connection === 'close') {
const shouldReconnect = (lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
console.log('Connection closed due to ', lastDisconnect.error, ', reconnecting ', shouldReconnect)
if(shouldReconnect) {
connectToWhatsApp()
}
} else if(connection === 'open') {
console.log('✅ Successfully connected to WhatsApp!')
}
})
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const m of messages) {
if (!m.message) continue
console.log('📱 New message:', JSON.stringify(m, undefined, 2))
// Auto reply
await sock.sendMessage(m.key.remoteJid!, {
text: 'Hello! I am a WhatsApp bot using Baileys 🤖'
})
}
})
sock.ev.on('creds.update', saveCreds)
}
connectToWhatsApp()
Disclaimer: This documentation is still in beta, so there may be errors or inconsistencies
WhatsApp provides a multi-device API that allows Baileys to authenticate as a secondary WhatsApp client via QR code or pairing code.
[!TIP]
Customize the browser name using theBrowsersconstant. See available configurations below.
const { default: makeWASocket, Browsers } = require("baileys")
const sock = makeWASocket({
browser: Browsers.ubuntu('My App'),
printQRInTerminal: true
})
After successful connection, a QR code will appear in the terminal. Scan it with WhatsApp on your phone to login.
[!IMPORTANT]
Pairing code is not part of the Mobile API. It allows WhatsApp Web connection without QR code, but only one device. See WhatsApp FAQ.
Phone number must be without +, (), or -, and include the country code.
const { default: makeWASocket } = require("baileys")
const sock = makeWASocket({
printQRInTerminal: false
})
// Normal Pairing
if (!sock.authState.creds.registered) {
const number = '6281251767935'
const code = await sock.requestPairingCode(number)
console.log('🔑 Pairing Code:', code)
}
// Custom Pairing
if (!sock.authState.creds.registered) {
const pair = "BAIL1234" // 8 characters
const number = '6281251767935'
const code = await sock.requestPairingCode(number, pair)
console.log('🔑 Custom Pairing Code:', code)
}
syncFullHistory to true.const { default: makeWASocket, Browsers } = require("baileys")
const sock = makeWASocket({
browser: Browsers.macOS('Desktop'),
syncFullHistory: true
})
For group usage, implement group metadata caching:
const { default: makeWASocket } = require("baileys")
const NodeCache = require('node-cache')
const groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false })
const sock = makeWASocket({
cachedGroupMetadata: async (jid) => groupCache.get(jid)
})
sock.ev.on('groups.update', async ([event]) => {
const metadata = await sock.groupMetadata(event.id)
groupCache.set(event.id, metadata)
})
sock.ev.on('group-participants.update', async (event) => {
const metadata = await sock.groupMetadata(event.id)
groupCache.set(event.id, metadata)
})
Improve message sending and poll vote decryption with store:
const sock = makeWASocket({
getMessage: async (key) => await getMessageFromStore(key)
})
Disable online status to receive notifications:
const sock = makeWASocket({
markOnlineOnConnect: false
})
Avoid repeated QR code scanning by saving credentials:
const makeWASocket = require("baileys").default
const { useMultiFileAuthState } = require("baileys")
async function connect() {
const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
const sock = makeWASocket({ auth: state })
sock.ev.on('creds.update', saveCreds)
}
connect()
[!IMPORTANT]
useMultiFileAuthStatesaves auth state in a folder. For production, use a SQL/No-SQL database and manage key updates carefully.
const sock = makeWASocket()
sock.ev.on('messages.upsert', ({ messages }) => {
console.log('Got messages:', messages)
})
const makeWASocket = require("baileys").default
const { DisconnectReason, useMultiFileAuthState } = require("baileys")
const { Boom } = require('@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', (update) => {
const { connection, lastDisconnect } = update
if(connection === 'close') {
const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
console.log('Connection closed due to ', lastDisconnect.error, ', reconnecting ', shouldReconnect)
if(shouldReconnect) {
connectToWhatsApp()
}
} else if(connection === 'open') {
console.log('Connection opened')
}
})
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const m of messages) {
console.log(JSON.stringify(m, undefined, 2))
console.log('Reply to', m.key.remoteJid)
await sock.sendMessage(m.key.remoteJid!, { text: 'Hello World' })
}
})
sock.ev.on('creds.update', saveCreds)
}
connectToWhatsApp()
By default, poll votes are encrypted and handled in messages.update:
sock.ev.on('messages.update', event => {
for(const { key, update } of event) {
if(update.pollUpdates) {
const pollCreation = await getMessage(key)
if(pollCreation) {
console.log(
'poll update received, aggregation: ',
getAggregateVotesInPollMessage({
message: pollCreation,
pollUpdates: update.pollUpdates,
})
)
}
}
}
})
getMessage is a store implementation (on your side).
connection.update will be triggered requesting sock restart.messaging.history-set.Baileys does not include default storage for chats, contacts, or messages. However, a simple in-memory implementation is provided. The store listens to chat updates, new messages, etc., to keep data up to date.
[!IMPORTANT]
I highly recommend building your own data store, as storing entire chat history in memory is very wasteful of RAM.
const makeWASocket = require("baileys").default
const { makeInMemoryStore } = require("baileys")
const store = makeInMemoryStore({ })
store.readFromFile('./baileys_store.json')
setInterval(() => {
store.writeToFile('./baileys_store.json')
}, 10_000)
const sock = makeWASocket({ })
store.bind(sock.ev)
sock.ev.on('chats.upsert', () => {
console.log('got chats', store.chats.all())
})
sock.ev.on('contacts.upsert', () => {
console.log('got contacts', Object.values(store.contacts))
})
The store also provides simple functions like loadMessages to speed up data retrieval.
id is the WhatsApp ID, also called jid, for the person or group you're sending a message to.
Format: [country code][phone number]@s.whatsapp.net
Example for a person: +19999999999@s.whatsapp.net.
For groups: 123456789-123345@g.us.
For broadcast lists: [creation timestamp]@broadcast.
For stories: status@broadcast.
getContentType - Returns the content type of a messagegetDevice - Returns the device from a messagemakeCacheableSignalKeyStore - Speeds up auth storedownloadContentFromMessage - Download content from a messageSend all types of messages with a single function.
See supported message content in the section below.
See options like quote in the example below.
const jid: string
const content: AnyMessageContent
const options: MiscMessageGenerationOptions
sock.sendMessage(jid, content, options)
await sock.sendMessage(jid, { text: 'hello world' })
await sock.sendMessage(jid, { text: 'hello world' }, { quoted: message })
@number to mention in text, optional.
await sock.sendMessage(
jid,
{
text: '@12345678901',
mentions: ['12345678901@s.whatsapp.net']
}
)
Requires a message object, get from store or use message object.
const msg = getMessageFromStore() // implement yourself
await sock.sendMessage(jid, { forward: msg })
await sock.sendMessage(
jid,
{
location: {
degreesLatitude: 24.121231,
degreesLongitude: 55.1121221
}
}
)
const vcard = 'BEGIN:VCARD\n'
+ 'VERSION:3.0\n'
+ 'FN:Jeff Singh\n'
+ 'ORG:Ashoka Uni;\n'
+ 'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n'
+ 'END:VCARD'
await sock.sendMessage(
id,
{
contacts: {
displayName: 'Jeff',
contacts: [{ vcard }]
}
}
)
Requires message key, get from store or use key object.
await sock.sendMessage(
jid,
{
react: {
text: '💖', // empty to remove reaction
key: message.key
}
}
)
Requires message key.
Time:
| Time | Seconds |
|---|---|
| 24h | 86,400 |
| 7d | 604,800 |
| 30d | 2,592,000 |
await sock.sendMessage(
jid,
{
pin: {
type: 1, // 0 to unpin
time: 86400,
key: message.key
}
}
)
await sock.sendMessage(
jid,
{
poll: {
name: 'My Poll',
values: ['Option 1', 'Option 2', ...],
selectableCount: 1,
toAnnouncementGroup: false // or true
}
}
)
link-preview-js with npm i link-preview-js.await sock.sendMessage(
jid,
{
text: 'Hello, this is sent using https://npmjs.com/wileys'
}
)
Sending media (video, sticker, image) is easier & more efficient.
[!NOTE] In media messages, you can use
{ stream: Stream }or{ url: Url }or Buffer directly, see examples below.
Baileys doesn't load the entire buffer into memory; it encrypts as a stream.
[!TIP] Use Stream or Url to save memory.
WA doesn't support .gif, send as .mp4 with gifPlayback flag.
await sock.sendMessage(
jid,
{
video: fs.readFileSync('Media/ma_gif.mp4'),
caption: 'hello world',
gifPlayback: true
}
)
await sock.sendMessage(
id,
{
video: {
url: './Media/ma_gif.mp4'
},
caption: 'hello world',
ptv: false // true for video note
}
)
Convert with ffmpeg: codec: libopus, ac: 1, avoid_negative_ts, make_zero.
Example: ffmpeg -i input.mp4 -avoid_negative_ts make_zero -ac 1 output.ogg
await sock.sendMessage(
jid,
{
audio: {
url: './Media/audio.mp3'
},
mimetype: 'audio/mp4'
}
)
await sock.sendMessage(
id,
{
image: {
url: './Media/ma_img.png'
},
caption: 'hello world'
}
)
Add viewOnce: true for all messages above.
await sock.sendMessage(
id,
{
image: {
url: './Media/ma_img.png'
},
viewOnce: true, // works with video, audio too
caption: 'hello world'
}
)
const msg = await sock.sendMessage(jid, { text: 'hello world' })
await sock.sendMessage(jid, { delete: msg.key })
Note: Delete for self is supported via chatModify, see Modify Chats.
Use editable content.
await sock.sendMessage(jid, {
text: 'updated text here',
edit: response.key,
});
Thumbnail is automatic for images & stickers if you add jimp or sharp (npm i jimp or npm i sharp).
For video, you need ffmpeg installed.
To save received media:
import { createWriteStream } from 'fs'
import { downloadMediaMessage, getContentType } from 'baileys'
sock.ev.on('messages.upsert', async ({ messages: [m] }) => {
if (!m.message) return
const messageType = getContentType(m)
if (messageType === 'imageMessage') {
const stream = await downloadMediaMessage(
m,
'stream', // or 'buffer'
{ },
{
logger,
reuploadRequest: sock.updateMediaMessage
}
)
const writeStream = createWriteStream('./my-download.jpeg')
stream.pipe(writeStream)
}
})
WA deletes old media from servers. Re-upload with:
await sock.updateMediaMessage(msg)
Get callId and callFrom from the call event.
await sock.rejectCall(callId, callFrom)
A set of message keys must be explicitly marked as read.
const key: WAMessageKey
await sock.readMessages([key]) // can be multiple keys
Message ID is a unique identifier. Access with message.key.id.
presence can be: available, composing, recording, paused, unavailable.
Valid for 10 seconds. Let jid know whether you're online, offline, typing, etc.
await sock.sendPresenceUpdate('available', jid)
[!NOTE] If desktop client is active, WA doesn't send push notifications. To receive notifications, set offline with
sock.sendPresenceUpdate('unavailable').
WA uses encrypted communication for chat/app updates.
[!IMPORTANT] If you update incorrectly, WA may logout from all devices.
const lastMsgInChat = await getLastMessageInChat(jid) // implement yourself
await sock.chatModify({ archive: true, lastMessages: [lastMsgInChat] }, jid)
Supported times:
| Time | Milliseconds |
|---|---|
| Remove | null |
| 8h | 86,400,000 |
| 7d | 604,800,000 |
// Mute 8 hours
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
// Unmute
await sock.chatModify({ mute: null }, jid)
const lastMsgInChat = await getLastMessageInChat(jid)
// mark unread
await sock.chatModify({ markRead: false, lastMessages: [lastMsgInChat] }, jid)
await sock.chatModify(
{
clear: {
messages: [
{
id: 'ATWYHDNNWU81732J',
fromMe: true,
timestamp: '1654823909'
}
]
}
},
jid
)
const lastMsgInChat = await getLastMessageInChat(jid)
await sock.chatModify({
delete: true,
lastMessages: [
{
key: lastMsgInChat.key,
messageTimestamp: lastMsgInChat.messageTimestamp
}
]
},
jid
)
await sock.chatModify({
pin: true // or false to unpin
},
jid
)
await sock.chatModify({
star: {
messages: [
{
id: 'messageID',
fromMe: true // or false
}
],
star: true // true: Star; false: Unstar
}
},
jid
)
Ephemeral:
| Time | Seconds |
|---|---|
| Remove | 0 |
| 24h | 86,400 |
| 7d | 604,800 |
| 90d | 7,776,000 |
Use seconds, default is 7 days.
// Enable disappearing messages
await sock.sendMessage(
jid,
{ disappearingMessagesInChat: WA_DEFAULT_EPHEMERAL }
)
// Send as disappearing message
await sock.sendMessage(jid, { text: 'hello' }, { ephemeralExpiration: WA_DEFAULT_EPHEMERAL })
// Disable
await sock.sendMessage(
jid,
{ disappearingMessagesInChat: false }
)
const [result] = await sock.onWhatsApp(jid)
if (result.exists) console.log(`${jid} exists on WhatsApp, as jid: ${result.jid}`)
Requires oldest message in chat.
const msg = await getOldestMessageInChat(jid) // implement yourself
await sock.fetchMessageHistory(
50, // count (max: 50 per query)
msg.key,
msg.messageTimestamp
)
Messages are received in the messaging.history-set event.
const status = await sock.fetchStatus(jid)
console.log('status: ' + status)
// low resolution
const ppUrl = await sock.profilePictureUrl(jid)
console.log(ppUrl)
// high resolution
const ppUrl = await sock.profilePictureUrl(jid, 'image')
const profile = await sock.getBusinessProfile(jid)
console.log('business description: ' + profile.description + ', category: ' + profile.category)
sock.ev.on('presence.update', console.log)
await sock.presenceSubscribe(jid)
await sock.updateProfileStatus('Hello World!')
await sock.updateProfileName('My Name')
[!NOTE] Like media messages, use
{ stream: Stream }or{ url: Url }or Buffer, see Media Messages.
await sock.updateProfilePicture(jid, { url: './new-profile-picture.jpeg' })
await sock.removeProfilePicture(jid)
To modify group properties, you must be an admin.
const group = await sock.groupCreate('My Group', ['1234@s.whatsapp.net', '4564@s.whatsapp.net'])
console.log('group created with id: ' + group.gid)
await sock.sendMessage(group.id, { text: 'hello everyone' })
await sock.groupParticipantsUpdate(
jid,
['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
'add' // replace with 'remove', 'demote', or 'promote'
)
await sock.groupUpdateSubject(jid, 'New Subject!')
await sock.groupUpdateDescription(jid, 'New Description!')
// Only admins can send messages
await sock.groupSettingUpdate(jid, 'announcement')
// Everyone can send
await sock.groupSettingUpdate(jid, 'not_announcement')
// Everyone can modify group settings
await sock.groupSettingUpdate(jid, 'unlocked')
// Only admins can modify settings
await sock.groupSettingUpdate(jid, 'locked')
await sock.groupLeave(jid)
Create link: 'https://chat.whatsapp.com/' + code.
const code = await sock.groupInviteCode(jid)
console.log('group code: ' + code)
const code = await sock.groupRevokeInvite(jid)
console.log('New group code: ' + code)
Code without https://chat.whatsapp.com/.
const response = await sock.groupAcceptInvite(code)
console.log('joined to: ' + response)
const response = await sock.groupGetInviteInfo(code)
console.log('group info: ' + response)
const metadata = await sock.groupMetadata(jid)
console.log(metadata.id + ', title: ' + metadata.subject + ', description: ' + metadata.desc)
const response = await sock.groupAcceptInviteV4(jid, groupInviteMessage)
console.log('joined to: ' + response)
const response = await sock.groupRequestParticipantsList(jid)
console.log(response)
const response = await sock.groupRequestParticipantsUpdate(
jid,
['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
'approve' // or 'reject'
)
console.log(response)
const response = await sock.groupFetchAllParticipating()
console.log(response)
Ephemeral:
| Time | Seconds |
|---|---|
| Remove | 0 |
| 24h | 86,400 |
| 7d | 604,800 |
| 90d | 7,776,000 |
await sock.groupToggleEphemeral(jid, 86400)
await sock.groupMemberAddMode(
jid,
'all_member_add' // or 'admin_add'
)
await sock.updateBlockStatus(jid, 'block') // Block
await sock.updateBlockStatus(jid, 'unblock') // Unblock
const privacySettings = await sock.fetchPrivacySettings(true)
console.log('privacy settings: ' + privacySettings)
const response = await sock.fetchBlocklist()
console.log(response)
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateLastSeenPrivacy(value)
const value = 'all' // 'match_last_seen'
await sock.updateOnlinePrivacy(value)
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateProfilePicturePrivacy(value)
const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateStatusPrivacy(value)
const value = 'all' // 'none'
await sock.updateReadReceiptsPrivacy(value)
const value = 'all' // 'contacts' | 'contact_blacklist'
await sock.updateGroupsAddPrivacy(value)
Ephemeral:
| Time | Seconds |
|---|---|
| Remove | 0 |
| 24h | 86,400 |
| 7d | 604,800 |
| 90d | 7,776,000 |
const ephemeral = 86400
await sock.updateDefaultDisappearingMode(ephemeral)
Messages can be sent to broadcasts & stories. Add message options:
await sock.sendMessage(
jid,
{
image: {
url: url
},
caption: caption
},
{
backgroundColor: backgroundColor,
font: font,
statusJidList: statusJidList,
broadcast: true
}
)
Message body can be extendedTextMessage, imageMessage, etc., see Media Messages.
broadcast: true enables broadcast mode.
statusJidList: list of recipients.
Broadcast ID: 12345678@broadcast.
const bList = await sock.getBroadcastListInfo('1234@broadcast')
console.log(`list name: ${bList.name}, recipients: ${bList.recipients}`)
Baileys is designed for custom functions. Add your own extensions without forking.
const sock = makeWASocket({
logger: P({ level: 'debug' }),
})
This will display WhatsApp messages in the console.
[!TIP] Learn about WhatsApp protocol with Libsignal Protocol and Noise Protocol.
Example: Track phone battery percentage. Enable logs, battery message appears:
{
"level": 10,
"fromMe": false,
"frame": {
"tag": "ib",
"attrs": {
"from": "@s.whatsapp.net"
},
"content": [
{
"tag": "edge_routing",
"attrs": {},
"content": [
{
"tag": "routing_info",
"attrs": {},
"content": {
"type": "Buffer",
"data": [8,2,8,5]
}
}
]
}
]
},
"msg":"communication"
}
'frame' has: tag (frame type), attrs (metadata), content (data).
[!TIP] See
onMessageReceivedfunction insocket.ts.
// For 'edge_routing' tag
sock.ws.on('CB:edge_routing', (node: BinaryNode) => { })
// With id 'abcd'
sock.ws.on('CB:edge_routing,id:abcd', (node: BinaryNode) => { })
// With routing_info node
sock.ws.on('CB:edge_routing,id:abcd,routing_info', (node: BinaryNode) => { })
Distributed under the GPL-3.0 License. See LICENSE for more information.
FAQs
WhatsApp Web API Library
We found that @k4la/baileys demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.

Security News
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.

Research
/Security News
Campaign of 108 extensions harvests identities, steals sessions, and adds backdoors to browsers, all tied to the same C2 infrastructure.