Skip to main content
Baileys provides two methods to authenticate your WhatsApp Web client: scanning a QR code or using a pairing code. Both methods connect your bot as a secondary device to an existing WhatsApp account.

Authentication methods

WhatsApp’s multi-device API allows Baileys to authenticate as a second WhatsApp client by either:
  • QR code: Scan a code displayed in your terminal with WhatsApp on your phone
  • Pairing code: Enter a code in WhatsApp mobile settings to link without scanning
Pairing code is not the Mobile API. It’s simply an alternative method to connect WhatsApp Web without QR code scanning.

QR code authentication

The QR code method displays a scannable code in your terminal that you scan with WhatsApp on your phone.

Basic QR code setup

import makeWASocket from '@whiskeysockets/baileys'

const sock = makeWASocket({
    printQRInTerminal: true
})

Customizing browser appearance

You can customize how your client appears in WhatsApp’s “Linked Devices” list:
import makeWASocket, { Browsers } from '@whiskeysockets/baileys'

const sock = makeWASocket({
    browser: Browsers.ubuntu('My App'),
    printQRInTerminal: true
})
browser: Browsers.ubuntu('My App')

Pairing code authentication

The pairing code method lets users link their device by entering a code in WhatsApp settings, avoiding the need to scan a QR code.

Basic pairing code setup

1

Disable QR terminal printing

Set printQRInTerminal to false when creating the socket:
const sock = makeWASocket({
    printQRInTerminal: false
})
2

Check registration status

Check if credentials are already registered before requesting a pairing code:
if (!sock.authState.creds.registered) {
    // Request pairing code
}
3

Request pairing code

Request a pairing code using the phone number (country code + number, no special characters):
const phoneNumber = '1234567890' // No +, (), or -
const code = await sock.requestPairingCode(phoneNumber)
console.log(`Pairing code: ${code}`)
4

Enter code in WhatsApp

The user enters this code in WhatsApp > Settings > Linked Devices > Link a Device > Link with Phone Number

Complete pairing code example

import makeWASocket from '@whiskeysockets/baileys'

async function connectWithPairing() {
    const sock = makeWASocket({
        printQRInTerminal: false
    })
    
    if (!sock.authState.creds.registered) {
        const phoneNumber = '1234567890'
        const code = await sock.requestPairingCode(phoneNumber)
        console.log(`Your pairing code: ${code}`)
    }
}
The phone number must include only digits with the country code. Do not include +, (), - or spaces.

Saving and restoring sessions

To avoid re-authenticating every time, save the authentication state to disk using useMultiFileAuthState.

Using multi-file auth state

import makeWASocket, { useMultiFileAuthState } from '@whiskeysockets/baileys'

const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')

const sock = makeWASocket({ 
    auth: state,
    printQRInTerminal: true 
})

// Save credentials whenever they update
sock.ev.on('creds.update', saveCreds)
The useMultiFileAuthState function stores authentication data in a folder. For production applications, implement a custom auth state handler using a database instead.

How auth state works

The authentication state contains two components:
  • state.creds: Authentication credentials including identity keys and registration info
  • state.keys: Signal protocol keys for message encryption/decryption
export type AuthenticationState = {
    creds: AuthenticationCreds
    keys: SignalKeyStore
}

Key state management

Whenever a message is sent or received, the auth keys (state.keys) will update due to Signal protocol session management. You must save these updates by implementing creds.update event handler. Failing to do so will prevent messages from being delivered and cause decryption failures.
sock.ev.on('creds.update', saveCreds)
The useMultiFileAuthState function handles this automatically, but custom implementations must be careful with key state management.

Receiving full history

By default, Baileys receives limited message history. To receive more:
1

Enable full history sync

Set syncFullHistory to true in socket config:
const sock = makeWASocket({
    syncFullHistory: true
})
2

Use desktop browser config

Desktop connections receive more history than mobile:
const sock = makeWASocket({
    browser: Browsers.macOS('Desktop'),
    syncFullHistory: true
})

Authentication with custom configuration

Here’s a complete example combining authentication with common configurations:
import makeWASocket, { 
    useMultiFileAuthState, 
    Browsers,
    DisconnectReason 
} from '@whiskeysockets/baileys'
import { Boom } from '@hapi/boom'

async function connectToWhatsApp() {
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
    
    const sock = makeWASocket({
        auth: state,
        browser: Browsers.ubuntu('My WhatsApp Bot'),
        printQRInTerminal: true,
        syncFullHistory: true
    })
    
    sock.ev.on('connection.update', (update) => {
        const { connection, lastDisconnect } = update
        
        if (connection === 'close') {
            const shouldReconnect = 
                (lastDisconnect?.error as Boom)?.output?.statusCode !== 
                DisconnectReason.loggedOut
            
            if (shouldReconnect) {
                connectToWhatsApp()
            }
        } else if (connection === 'open') {
            console.log('Connected successfully')
        }
    })
    
    sock.ev.on('creds.update', saveCreds)
}

connectToWhatsApp()

Custom auth state implementation

For production systems, implement a custom auth state using a database:
import { AuthenticationState, SignalDataTypeMap } from '@whiskeysockets/baileys'

function createDatabaseAuthState(): AuthenticationState {
    return {
        creds: await loadCredsFromDatabase(),
        keys: {
            get: async (type, ids) => {
                // Load keys from database
                return await db.getKeys(type, ids)
            },
            set: async (data) => {
                // Save keys to database
                await db.saveKeys(data)
            }
        }
    }
}
The useMultiFileAuthState implementation in src/Utils/use-multi-file-auth-state.ts serves as an excellent reference for building custom auth state handlers.

Next steps

Connection management

Learn about connection lifecycle and reconnection strategies

Event handling

Handle messages, connection updates, and other events

Build docs developers (and LLMs) love