npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

emnex-baileys

v2.5.11

Published

A lightweight, full-featured WhatsApp Web API library for Node.js

Downloads

300

Readme

npm version npm downloads License

Features

  • 📱 Multi-device — connects directly to the WhatsApp Web multi-device API; no browser, Selenium, or phone-tethering required.
  • 🔌 WebSocket-based — fast and lightweight, with a small dependency footprint.
  • 🔑 Two auth flows — link a device with a QR code or an 8-character pairing code.
  • 💬 Rich messaging — text, images, video, audio/voice notes, documents, locations, contacts, and reactions.
  • 👥 Groups — create, manage participants, invite links, and metadata.
  • 📢 Channels & Status — follow/manage newsletters (channels) and post to status/stories.
  • 🟦 TypeScript — ships with full type definitions.

Disclaimer

This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with WhatsApp or any of its subsidiaries or affiliates. Use at your own discretion. Do not spam people with this. We discourage any stalkerware, bulk or automated messaging usage.

Credits & Acknowledgements

emnex-baileys (maintained by Emnex Tech) stands on the shoulders of others:

  • Baileys by WhiskeySockets — the original, brilliant WhatsApp Web API implementation that makes all of this possible.
  • gifted-baileys by Gifted Tech — the fork this project is based on. Big thanks to Gifted Tech for their work.

This library is MIT-licensed and retains the copyright notices of the projects above. Please support and credit the upstream authors.

Table of Contents

Installation

npm install emnex-baileys

Or using yarn:

yarn add emnex-baileys

Requirements: Node.js 20 or newer.

Quick Start

CommonJS (Recommended)

const { default: makeWASocket, useMultiFileAuthState, Browsers, DisconnectReason } = require('emnex-baileys')

async function start() {
  const { state, saveCreds } = await useMultiFileAuthState('auth')

  const sock = makeWASocket({
    auth: state,
    browser: Browsers.ubuntu('Chrome'),
  })

  // persist updated credentials
  sock.ev.on('creds.update', saveCreds)

  sock.ev.on('connection.update', (update) => {
    const { connection } = update
    if (connection === 'open') console.log('✅ connected')
  })

  sock.ev.on('messages.upsert', async ({ messages }) => {
    const msg = messages[0]
    if (!msg.message || msg.key.fromMe) return
    if (msg.message.conversation === 'hi') {
      await sock.sendMessage(msg.key.remoteJid, { text: 'Hello 👋' })
    }
  })
}

start()

ES Modules / TypeScript

import pkg from 'emnex-baileys'
const { default: makeWASocket, useMultiFileAuthState, Browsers } = pkg

Getting Started: a complete bot

Here's a complete, copy-paste echo bot you can run right now. It renders a QR code in your terminal, reconnects automatically, and replies to any incoming text message.

npm install emnex-baileys qrcode-terminal
// bot.js
const {
  default: makeWASocket,
  useMultiFileAuthState,
  Browsers,
  DisconnectReason,
} = require('emnex-baileys')
const qrcode = require('qrcode-terminal')

async function startBot() {
  const { state, saveCreds } = await useMultiFileAuthState('auth')

  const sock = makeWASocket({
    auth: state,
    browser: Browsers.ubuntu('Chrome'),
  })

  // 1. persist credentials whenever they change
  sock.ev.on('creds.update', saveCreds)

  // 2. handle connection lifecycle + QR rendering
  sock.ev.on('connection.update', (update) => {
    const { connection, lastDisconnect, qr } = update

    if (qr) qrcode.generate(qr, { small: true }) // scan this with WhatsApp

    if (connection === 'open') {
      console.log('✅ Bot connected and ready!')
    } else if (connection === 'close') {
      const shouldReconnect =
        lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut
      console.log('Connection closed.', shouldReconnect ? 'Reconnecting…' : 'Logged out.')
      if (shouldReconnect) startBot()
    }
  })

  // 3. reply to incoming messages
  sock.ev.on('messages.upsert', async ({ messages }) => {
    const msg = messages[0]
    if (!msg.message || msg.key.fromMe) return

    const text =
      msg.message.conversation || msg.message.extendedTextMessage?.text || ''
    if (!text) return

    await sock.sendMessage(msg.key.remoteJid, { text: `You said: ${text}` }, { quoted: msg })
  })
}

startBot()

Run it:

node bot.js

Scan the QR code (WhatsApp → Settings → Linked Devices → Link a Device). Once you see ✅ Bot connected and ready!, message the linked account from another phone and it will echo your text back. The auth/ folder keeps you logged in, so you only scan once.

Authentication

useMultiFileAuthState('auth') stores the session in a folder (here ./auth) so you only authenticate once. There are two ways to link a device.

QR Code

The printQRInTerminal option is deprecated and no longer prints automatically. Listen to the connection.update event and render the qr string yourself, e.g. with qrcode-terminal:

const qrcode = require('qrcode-terminal')

sock.ev.on('connection.update', (update) => {
  const { qr } = update
  if (qr) qrcode.generate(qr, { small: true })
})

Then open WhatsApp → Settings → Linked Devices → Link a Device and scan it.

Pairing Code

Instead of a QR, request an 8-character pairing code for a phone number (international format, digits only):

if (!sock.authState.creds.registered) {
  const code = await sock.requestPairingCode('254700000000')
  console.log('Pairing code:', code) // enter this in WhatsApp → Link a Device → Link with phone number
}

// optional: supply your own 8-char custom code
// const code = await sock.requestPairingCode('254700000000', 'EMNEX123')

Connection & Reconnection

Handle drops and re-link based on the disconnect reason:

const { DisconnectReason } = require('emnex-baileys')

sock.ev.on('connection.update', (update) => {
  const { connection, lastDisconnect } = update
  if (connection === 'close') {
    const code = lastDisconnect?.error?.output?.statusCode
    if (code !== DisconnectReason.loggedOut) {
      start() // reconnect
    } else {
      console.log('Logged out — delete the ./auth folder and re-authenticate.')
    }
  }
})

Events

Subscribe with sock.ev.on(eventName, handler). Commonly used events:

| Event | Fires when | |-------|------------| | connection.update | Connection state changes (incl. qr, connection, lastDisconnect) | | creds.update | Auth credentials change — pair with saveCreds | | messages.upsert | New or appended messages arrive | | messages.update | Existing messages are edited / status changes | | message-receipt.update | Read / delivery receipts | | groups.update | Group metadata changes | | group-participants.update | Members added / removed / promoted | | contacts.update | Contact info changes | | presence.update | Online / typing presence |

Sending Messages

All sends go through sock.sendMessage(jid, content, options?). The jid is [email protected] for users, [email protected] for groups, or id@newsletter for channels.

const jid = '[email protected]'

// Text
await sock.sendMessage(jid, { text: 'Hello from emnex-baileys' })

// Reply to a message
await sock.sendMessage(jid, { text: 'replying' }, { quoted: someMsg })

// Image (Buffer or { url })
await sock.sendMessage(jid, { image: { url: './photo.jpg' }, caption: 'nice 📸' })

// Video
await sock.sendMessage(jid, { video: { url: './clip.mp4' }, caption: 'watch' })

// Audio / voice note
await sock.sendMessage(jid, { audio: { url: './voice.mp3' }, mimetype: 'audio/mp4', ptt: true })

// Document
await sock.sendMessage(jid, { document: { url: './file.pdf' }, mimetype: 'application/pdf', fileName: 'file.pdf' })

// Location
await sock.sendMessage(jid, { location: { degreesLatitude: -1.286389, degreesLongitude: 36.817223 } })

// Contact (vCard)
await sock.sendMessage(jid, {
  contacts: {
    displayName: 'Emnex',
    contacts: [{ vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:Emnex\nTEL;type=CELL:254700000000\nEND:VCARD' }],
  },
})

// Reaction (emoji on a message key)
await sock.sendMessage(jid, { react: { text: '🔥', key: someMsg.key } })

Note: For interactive buttons, use the separate gifted-btns package.

Receiving & Downloading Media

const { downloadMediaMessage } = require('emnex-baileys')

sock.ev.on('messages.upsert', async ({ messages }) => {
  const msg = messages[0]
  const type = Object.keys(msg.message || {})[0]
  if (['imageMessage', 'videoMessage', 'audioMessage', 'documentMessage'].includes(type)) {
    const buffer = await downloadMediaMessage(msg, 'buffer', {})
    // write `buffer` to disk, re-upload, etc.
  }
})

Group Management

// Create a group
const group = await sock.groupCreate('My Group', ['[email protected]'])

// Add / remove / promote / demote participants
await sock.groupParticipantsUpdate(group.id, ['[email protected]'], 'add')    // 'remove' | 'promote' | 'demote'

// Update subject
await sock.groupUpdateSubject(group.id, 'New Name')

// Read metadata
const meta = await sock.groupMetadata(group.id)

// Invite links
const code = await sock.groupInviteCode(group.id)   // -> https://chat.whatsapp.com/<code>
await sock.groupAcceptInvite('<code>')

// Leave
await sock.groupLeave(group.id)

Channels / Newsletters

Channels use xxxxxxxxxxxx@newsletter JIDs.

// Resolve a channel invite link to its metadata (incl. the @newsletter id)
const meta = await sock.newsletterMetadata('invite', '0029Vb...')   // the code after /channel/
console.log(meta.id, meta.name)

// By JID
const byJid = await sock.newsletterMetadata('jid', '120363xxxxxxxxx@newsletter')

// Manage
await sock.newsletterCreate('My Channel', 'Description here')
await sock.newsletterFollow('120363xxxxxxxxx@newsletter')
await sock.newsletterUnfollow('120363xxxxxxxxx@newsletter')
await sock.newsletterMute('120363xxxxxxxxx@newsletter')
await sock.newsletterFetchMessages('120363xxxxxxxxx@newsletter', 50)

How to find your channel ID

Every message that arrives from a channel has a remoteJid ending in @newsletter. Log it from messages.upsert:

sock.ev.on('messages.upsert', ({ messages }) => {
  for (const m of messages) {
    const jid = m.key?.remoteJid
    if (jid?.endsWith('@newsletter')) console.log('channel:', jid)
  }
})

Alternatively, resolve a channel's invite link with newsletterMetadata('invite', code) (above).

Status / Stories

Send to your status broadcast by targeting status@broadcast and listing the recipients who should see it via statusJidList:

await sock.sendMessage(
  'status@broadcast',
  { image: { url: './story.jpg' }, caption: 'my status' },
  { statusJidList: ['[email protected]'] },
)

Profile & Privacy

await sock.updateProfileName('Emnex Bot')
await sock.updateProfileStatus('Powered by emnex-baileys')
await sock.updateProfilePicture('[email protected]', { url: './avatar.jpg' })
await sock.sendPresenceUpdate('available') // 'available' | 'unavailable' | 'composing' | 'recording'

Changelog

See CHANGELOG.md for release notes and the version history.

License

Released under the MIT License. Copyright © Emnex Tech, with retained notices for Gifted Tech and Baileys (WhiskeySockets). See Credits & Acknowledgements.