Skip to main content

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.
1

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
2

Create the bot file

Create a file named bot.ts with the following code:
bot.ts
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()
3

Run your bot

Start the bot using tsx:
npx tsx bot.ts
A QR code will appear in your terminal.
4

Scan the QR code

  1. Open WhatsApp on your phone
  2. Go to Settings > Linked Devices
  3. Tap Link a Device
  4. 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.
5

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

await sock.sendMessage('[email protected]', { 
  text: 'Hello, World!' 
})

Media 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

Build docs developers (and LLMs) love