Quick start
This guide walks you through creating a fully functional WhatsApp bot that can connect, authenticate, and respond to messages.
Create your first bot
Let’s build a simple bot that echoes received messages back to the sender.
Set up your project
Create a new directory and initialize a Node.js project: mkdir my-whatsapp-bot
cd my-whatsapp-bot
npm init -y
npm install @whiskeysockets/baileys
npm install -D tsx
Create the bot file
Create a file named bot.ts with the following code: import makeWASocket , {
DisconnectReason ,
useMultiFileAuthState
} from '@whiskeysockets/baileys'
import { Boom } from '@hapi/boom'
async function connectToWhatsApp () {
// Load authentication state from file
const { state , saveCreds } = await useMultiFileAuthState ( 'auth_info_baileys' )
const sock = makeWASocket ({
auth: state
})
// Handle connection updates
sock . ev . on ( 'connection.update' , ( update ) => {
const { connection , lastDisconnect , qr } = update
if ( qr ) {
console . log ( 'QR Code:' , qr )
// You can use qrcode-terminal to display the QR in terminal
}
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 successfully!' )
}
})
// Save credentials whenever they update
sock . ev . on ( 'creds.update' , saveCreds )
// Handle incoming messages
sock . ev . on ( 'messages.upsert' , async ({ messages }) => {
const message = messages [ 0 ]
// Ignore if no message content or if it's from us
if ( ! message . message || message . key . fromMe ) return
const text = message . message . conversation ||
message . message . extendedTextMessage ?. text
if ( text ) {
console . log ( 'Received message:' , text )
// Send a reply
await sock . sendMessage (
message . key . remoteJid ! ,
{ text: `You said: ${ text } ` }
)
}
})
}
// Start the bot
connectToWhatsApp ()
Run your bot
Start the bot using tsx: A QR code will appear in your terminal.
Scan the QR code
Open WhatsApp on your phone
Go to Settings > Linked Devices
Tap Link a Device
Scan the QR code displayed in your terminal
The authentication session will be saved in the auth_info_baileys folder. You won’t need to scan the QR code again on subsequent runs.
Test your bot
Send a message to your WhatsApp number from another device or contact. Your bot should echo it back!
Using pairing codes
Instead of QR codes, you can authenticate using a pairing code:
import makeWASocket , { useMultiFileAuthState } from '@whiskeysockets/baileys'
import readline from 'readline'
const rl = readline . createInterface ({
input: process . stdin ,
output: process . stdout
})
const question = ( text : string ) =>
new Promise < string >(( resolve ) => rl . question ( text , resolve ))
async function connectWithPairingCode () {
const { state , saveCreds } = await useMultiFileAuthState ( 'auth_info_baileys' )
const sock = makeWASocket ({
auth: state
})
// Request pairing code if not registered
if ( ! sock . authState . creds . registered ) {
const phoneNumber = await question ( 'Enter your phone number: ' )
const code = await sock . requestPairingCode ( phoneNumber )
console . log ( 'Pairing code:' , code )
}
sock . ev . on ( 'creds.update' , saveCreds )
// ... rest of your event handlers
}
connectWithPairingCode ()
The phone number must include the country code without +, (), or -. For example: 14155552671 for a US number.
Understanding authentication
Baileys uses useMultiFileAuthState to manage authentication credentials:
const { state , saveCreds } = await useMultiFileAuthState ( 'auth_info_baileys' )
This function:
Loads existing credentials from the specified folder
Creates the folder if it doesn’t exist
Returns a state object to pass to makeWASocket
Returns a saveCreds function to save updates
Always call saveCreds in the creds.update event. Failing to save credentials will cause authentication issues and message delivery failures.
Handling events
Baileys uses an event-driven architecture. Here are the most important events:
Connection updates
sock . ev . on ( 'connection.update' , ( update ) => {
const { connection , lastDisconnect , qr } = update
if ( qr ) {
console . log ( 'QR Code:' , qr )
// Display QR code to user using qrcode-terminal or other method
}
if ( connection === 'close' ) {
// Handle disconnection
} else if ( connection === 'open' ) {
// Handle successful connection
}
})
Incoming messages
sock . ev . on ( 'messages.upsert' , async ({ messages , type }) => {
for ( const message of messages ) {
// Process each message
console . log ( JSON . stringify ( message , null , 2 ))
}
})
Always use a loop to process messages from messages.upsert since the array can contain multiple messages.
Message updates
sock . ev . on ( 'messages.update' , ( updates ) => {
for ( const { key , update } of updates ) {
// Handle message status changes (delivered, read, deleted, etc.)
}
})
Sending messages
Baileys provides a single sendMessage function for all message types:
Text messages
import fs from 'fs'
// Image
await sock . sendMessage ( jid , {
image: { url: './photo.jpg' },
caption: 'Check out this image!'
})
// Video
await sock . sendMessage ( jid , {
video: fs . readFileSync ( './video.mp4' ),
caption: 'Watch this!'
})
// Audio
await sock . sendMessage ( jid , {
audio: { url: './audio.ogg' },
mimetype: 'audio/ogg; codecs=opus'
})
You can pass media as { url: string }, { stream: Stream }, or directly as a Buffer. Using streams or URLs is more memory-efficient.
Replying to messages
await sock . sendMessage (
jid ,
{ text: 'This is a reply' },
{ quoted: message }
)
Understanding WhatsApp IDs
WhatsApp uses JIDs (Jabber IDs) to identify users and groups:
Individual users : [country code][number]@s.whatsapp.net
Example: [email protected]
Groups : [group-id]@g.us
Example: [email protected]
Broadcast lists : [timestamp]@broadcast
Status/Stories : status@broadcast
You can extract the JID from a message using message.key.remoteJid.
Next steps
Now that you have a working bot, explore more advanced features:
Sending messages Learn about all message types and options
Events Master the event system for building complex bots
Groups Work with group chats and management
Configuration Optimize your socket configuration