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

whalibmob

v5.1.18

Published

WhatsApp library for interaction with WhatsApp Mobile API no web

Downloads

2,620

Readme

[!CAUTION] Use a dedicated phone number with this library. Connecting with a number that is already active on a real device will cause WhatsApp to log that device out.

[!IMPORTANT] This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with WhatsApp or any of its subsidiaries or affiliates. "WhatsApp" and related names are registered trademarks of their respective owners. Use at your own discretion.

  • whalibmob does not require a browser, Selenium, or any other external runtime — it communicates directly with WhatsApp using a TCP socket and the Noise Protocol handshake.
  • The library operates as a real iOS mobile device (iPhone), not as WhatsApp Web. It uses the Mobile API endpoint, which behaves differently from the Web API.
  • Signal Protocol encryption is fully inlined in pure JavaScript — no native binaries, no node-gyp, runs anywhere Node.js runs.

Install

npm install whalibmob

Install the CLI globally:

npm install -g whalibmob

Index


Library API

Connecting Account

Register a New Number

Registration is a one-time process. You need a phone number that can receive an SMS or voice call.

Step 1 — request a verification code

const {
  createNewStore, saveStore, requestSmsCode
} = require('whalibmob')
const path = require('path')
const fs   = require('fs')

const phone    = '919634847671'           // country code + number, no '+'
const sessDir  = path.join(process.env.HOME, '.waSession')
const sessFile = path.join(sessDir, phone + '.json')

fs.mkdirSync(sessDir, { recursive: true })

const store = createNewStore(phone)
saveStore(store, sessFile)

await requestSmsCode(store, 'sms')   // 'sms' | 'voice' | 'wa_old'

Step 2 — verify the code

const { loadStore, saveStore, verifyCode } = require('whalibmob')

const store  = loadStore(sessFile)
const result = await verifyCode(store, '123456')

if (result.status === 'ok') {
  saveStore(result.store, sessFile)
  console.log('registered')
}

Connect

const { WhalibmobClient } = require('whalibmob')
const path = require('path')

const client = new WhalibmobClient({
  sessionDir: path.join(process.env.HOME, '.waSession')
})

client.on('connected', () => {
  console.log('connected')
})

await client.init('919634847671')

Saving & Restoring Sessions

Sessions are automatically persisted to disk as JSON files under the sessionDir you provide. The file is named <phone>.json. On the next client.init() call the session is restored and no re-registration is needed.

const client = new WhalibmobClient({
  sessionDir: path.join(process.env.HOME, '.waSession')
})

// no need to register again — just connect
await client.init('919634847671')

[!NOTE] Each phone number uses its own session file. The library handles Signal Protocol key persistence automatically.

Handling Events

whalibmob uses the EventEmitter syntax for events.

Example to Start

const { WhalibmobClient } = require('whalibmob')
const path = require('path')

async function connect() {
  const client = new WhalibmobClient({
    sessionDir: path.join(process.env.HOME, '.waSession')
  })

  client.on('connected', async () => {
    console.log('connected')
    await client.sendText('[email protected]', 'Hello!')
  })

  client.on('disconnected', () => {
    console.log('disconnected — reconnecting...')
    setTimeout(() => connect(), 3000)
  })

  client.on('message', msg => {
    const d = msg.decoded
    if (d && d.type === 'text') console.log('message from', msg.from, d.text)
  })

  client.on('auth_failure', ({ reason }) => {
    console.error('session revoked:', reason)
    // re-register the number
  })

  await client.init('919634847671')
}

connect()

All Events

| Event | Payload | Description | |---|---|---| | connected | — | Session authenticated and ready | | disconnected | — | Connection closed | | reconnecting | { attempt, delay } | Lost connection, will retry | | reconnected | — | Connection restored | | auth_failure | { reason } | Session revoked or banned | | message | message object | Incoming message received | | receipt | { type, id, from } | Delivery / read / played receipt | | presence | { from, available } | Contact came online or went offline | | group_update | { type, groupJid, actor, participants, subject, timestamp } | Member added / removed / promoted / demoted, subject or settings changed | | notification | node object | Group or contact update notification | | call | { from } | Incoming call event | | chat_read | { jid, read } | Chat marked read (read: true) or unread (read: false) | | chat_muted | { jid, muted, until } | Chat muted or unmuted; until is epoch ms (−1 = indefinite) | | chat_pinned | { jid, pinned } | Chat pinned or unpinned | | chat_archived | { jid, archived } | Chat archived or unarchived | | message_starred | { msgId, chatJid, starred } | Message starred or unstarred | | stream_error | { reason } | Server sent a fatal stream error | | decrypt_error | { id, from, participant, err } | Failed to decrypt an incoming message | | session_refresh | { node } | Late re-authentication success; Signal session refreshed | | close | — | Underlying TCP socket closed | | error | Error | Unhandled transport error |

The message object contains:

{
  id:          string,   // unique message ID
  from:        string,   // sender JID — may be a LID (e.g. '[email protected]')
  participant: string,   // group member JID (groups only; equals from for DMs)
  ts:          number,   // Unix timestamp (seconds)
  node:        object,   // raw XML node — node.attrs.sender_pn holds the real phone JID
  decoded:     object,   // structured payload — shape depends on message type (see below)
}

[!NOTE] WhatsApp Multi-Device uses LID JIDs internally. The from field may be a LID like [email protected] rather than the real phone number. To get the actual phone number JID always read msg.node.attrs.sender_pn:

const spn = msg.node.attrs.sender_pn         // { user: '919634847671', server: 's.whatsapp.net' }
const phoneJid = spn.user + '@s.whatsapp.net' // '[email protected]'

The decoded object shape per message type:

// Text
{ type: 'text', text: string }

// Image
{ type: 'image', caption: string, url: string, mimetype: string, mediaKey: Buffer, directPath: string }

// Video
{ type: 'video', caption: string, url: string, mimetype: string, mediaKey: Buffer, directPath: string }

// Audio (music file)
{ type: 'audio', url: string, mimetype: string, mediaKey: Buffer, directPath: string }

// Voice note (push-to-talk)
{ type: 'voice', url: string, mimetype: string, mediaKey: Buffer, directPath: string }

// Document
{ type: 'document', fileName: string, url: string, mimetype: string, mediaKey: Buffer, directPath: string }

// Sticker
{ type: 'sticker', url: string, mimetype: string, mediaKey: Buffer, directPath: string }

// Reaction
{ type: 'reaction', emoji: string }

// Location
{ type: 'location', latitude: number, longitude: number, name: string, address: string, url: string }

// Contact (vCard)
{ type: 'contact', displayName: string, vcard: string }

// Protocol (revoke, ephemeral, etc.)
{ type: 'protocol', subtype: string }

Receiving Media

When a media message arrives, msg.decoded contains a CDN url and a mediaKey. The actual file is stored encrypted on WhatsApp's CDN and must be downloaded and decrypted.

Decryption uses two steps:

  1. HKDF-SHA256 expands mediaKey into IV, cipher key, and MAC key.
  2. AES-256-CBC decrypts the ciphertext; a 10-byte HMAC-SHA256 MAC is verified first.
const crypto = require('crypto')
const https  = require('https')
const http   = require('http')
const fs     = require('fs')
const path   = require('path')

// HKDF info strings per media type
const MEDIA_HKDF_INFO = {
  image:    'WhatsApp Image Keys',
  video:    'WhatsApp Video Keys',
  audio:    'WhatsApp Audio Keys',
  voice:    'WhatsApp Audio Keys',
  document: 'WhatsApp Document Keys',
  sticker:  'WhatsApp Image Keys',
}

function deriveMediaKeys(mediaKey, mediaType) {
  const info     = Buffer.from(MEDIA_HKDF_INFO[mediaType] || 'WhatsApp Image Keys', 'utf8')
  const expanded = Buffer.from(crypto.hkdfSync('sha256', mediaKey, Buffer.alloc(0), info, 112))
  return {
    iv:        expanded.slice(0,  16),
    cipherKey: expanded.slice(16, 48),
    macKey:    expanded.slice(48, 80),
  }
}

function decryptMedia(encrypted, mediaKey, mediaType) {
  const { iv, cipherKey, macKey } = deriveMediaKeys(mediaKey, mediaType)
  const ciphertext = encrypted.slice(0, -10)
  const fileMac    = encrypted.slice(-10)

  // Verify MAC
  const hmac     = crypto.createHmac('sha256', macKey)
  hmac.update(iv)
  hmac.update(ciphertext)
  const computed = hmac.digest().slice(0, 10)
  if (!computed.equals(fileMac)) throw new Error('MAC mismatch — corrupt file or wrong key')

  // Decrypt
  const decipher = crypto.createDecipheriv('aes-256-cbc', cipherKey, iv)
  return Buffer.concat([decipher.update(ciphertext), decipher.final()])
}

function downloadBuffer(url) {
  return new Promise((resolve, reject) => {
    const lib = url.startsWith('https') ? https : http
    const req = lib.get(url, { headers: { 'User-Agent': 'WhatsApp/2.26.7.75 A' } }, res => {
      if (res.statusCode !== 200) { res.resume(); return reject(new Error('HTTP ' + res.statusCode)) }
      const chunks = []
      res.on('data',  c => chunks.push(c))
      res.on('end',   () => resolve(Buffer.concat(chunks)))
      res.on('error', reject)
    })
    req.on('error', reject)
    req.setTimeout(30000, () => { req.destroy(); reject(new Error('timeout')) })
  })
}

async function downloadAndDecrypt(msgId, mediaType, url, mediaKey, opts) {
  const extensions = { image: '.jpg', video: '.mp4', audio: '.ogg', voice: '.ogg',
                       document: '', sticker: '.webp' }
  let ext = extensions[mediaType] || ''
  if (mediaType === 'document' && opts && opts.fileName) ext = path.extname(opts.fileName) || '.bin'

  const encrypted = await downloadBuffer(url)
  const decrypted = decryptMedia(encrypted, mediaKey, mediaType)

  const outPath = path.join('./media', msgId + ext)
  fs.mkdirSync('./media', { recursive: true })
  fs.writeFileSync(outPath, decrypted)
  return outPath
}

Using it in the message event:

const MEDIA_TYPES = new Set(['image', 'video', 'audio', 'voice', 'document', 'sticker'])

client.on('message', async msg => {
  const d = msg.decoded
  if (!d) return

  // Resolve the real phone JID (works even with LID from-fields)
  const spn       = msg.node && msg.node.attrs && msg.node.attrs.sender_pn
  const senderJid = spn ? (spn.user + '@s.whatsapp.net') : msg.from

  if (d.type === 'text') {
    console.log('text from', senderJid, ':', d.text)
  }

  if (MEDIA_TYPES.has(d.type) && d.url && d.mediaKey) {
    try {
      const filePath = await downloadAndDecrypt(msg.id, d.type, d.url, d.mediaKey, { fileName: d.fileName })
      console.log('saved', d.type, 'to', filePath)
    } catch (e) {
      console.error('media download failed:', e.message)
    }
  }
})

Sending Messages

Text Message

await client.sendText('[email protected]', 'Hello!')

Quote Message

For the simplest quoted reply use sendReply. If you need low-level control (e.g. quoting a non-text message), pass a contextInfo object directly into sendText:

// low-level: pass contextInfo manually inside sendText options
await client.sendText(
  '[email protected]',
  'This is a reply',
  {
    contextInfo: {
      quotedMessageId: 'ABCDEF123456',          // ID of the quoted message
      participant:     '[email protected]',  // sender of the quoted message
      remoteJid:       '[email protected]',  // chat JID
    }
  }
)

Mention User

await client.sendText(
  '[email protected]',
  '@919634847671 hello!',
  { mentions: ['[email protected]'] }
)

Reaction Message

// react to a message
await client.sendReaction('[email protected]', 'MSGID123', '👍')

// remove a reaction — pass empty string
await client.sendReaction('[email protected]', 'MSGID123', '')

Edit Message

[!NOTE] Editing is only possible within 15 minutes of the original send.

await client.editMessage(
  'MSGID123',                           // original message ID
  '[email protected]',
  'Corrected text here'
)

Delete Message

// delete for yourself only
await client.deleteMessage('MSGID123', '[email protected]', true, false)

// delete for everyone (revoke)
await client.deleteMessage('MSGID123', '[email protected]', true, true)

Forward Message

Forward text or a full media message (image, video, audio, document, sticker) without re-uploading. Pass a decoded message object from the message event to forward any media type.

// Forward text
await client.forwardMessage('[email protected]', 'text to forward')

// Forward any received message (full media, no re-upload)
client.on('message', async (msg) => {
  if (msg.decoded && msg.decoded.type !== 'text') {
    await client.forwardMessage('[email protected]', msg)
  }
})

Poll

Send a WhatsApp poll. selectableCount is how many options a voter may choose (0 = any).

const { id, encKey } = await client.sendPoll(
  '[email protected]',
  'Best language?',
  ['JavaScript', 'Python', 'Rust'],
  1            // voters may pick 1 option (0 = unlimited)
)
// encKey (32-byte Buffer) is needed to decrypt incoming poll votes

Quoted Reply

Send a text message that quotes (replies to) a specific earlier message. The recipient sees the original message highlighted above your reply.

// DM: senderJid is the same as the chat JID
await client.sendReply(
  '[email protected]',  // chat JID
  '3EB0XXXXXXXX',                 // ID of the quoted message
  '[email protected]',  // sender of the quoted message (same as chat for DMs)
  'Got it, thanks!'               // your reply text
)

// Group: senderJid is the group member who sent the quoted message
await client.sendReply(
  '[email protected]',      // group JID
  '3EB0XXXXXXXX',                 // ID of the quoted message
  '[email protected]',  // who sent the original message
  'Agreed!'
)

You can get the id of a received message from msg.id inside the message event.

Location Message

Send a GPS location pin. name and address are optional labels shown below the map preview.

// minimal — lat/lon only
await client.sendLocation('[email protected]', 48.8566, 2.3522)

// with name and address
await client.sendLocation('[email protected]', 48.8566, 2.3522, {
  name:    'Eiffel Tower',
  address: 'Champ de Mars, 5 Av. Anatole France, Paris'
})

// to a group
await client.sendLocation('[email protected]', 51.5074, -0.1278, {
  name: 'London'
})

Contact Message (vCard)

Send a contact card using the standard vCard v3 format. The recipient can save the contact directly from WhatsApp.

const vcard = [
  'BEGIN:VCARD',
  'VERSION:3.0',
  'FN:Alice Smith',
  'TEL;TYPE=CELL:+919634847671',
  'EMAIL:[email protected]',
  'END:VCARD'
].join('\n')

await client.sendContact('[email protected]', 'Alice Smith', vcard)

Media Messages

Image Message

// from file path
await client.sendImage('[email protected]', './photo.jpg', { caption: 'Look at this' })

// from Buffer
await client.sendImage('[email protected]', buffer, {
  caption: 'Photo',
  mimetype: 'image/jpeg'
})

Video Message

await client.sendVideo('[email protected]', './clip.mp4', { caption: 'Watch this' })

Audio Message

await client.sendAudio('[email protected]', './song.mp3')

Voice Note

// ptt: true renders the audio as a push-to-talk voice note with waveform
await client.sendAudio('[email protected]', './voice.ogg', { ptt: true })

Document Message

await client.sendDocument('[email protected]', './report.pdf', {
  fileName: 'Q1 Report.pdf'
})

Sticker Message

await client.sendSticker('[email protected]', './sticker.webp')

Status / Stories

// post a text Status to status@broadcast
await client.sendStatus('Good morning!')

Send States in Chat

Reading Messages

// mark all messages in a chat as read (sends IQ to server)
await client.markChatRead('[email protected]')

Mark Voice Message Played

Send a played receipt for a received voice note (push-to-talk audio). This tells the sender that you have listened to the message.

// msgId: ID of the audio message, from: JID of the sender
client.markMessagePlayed('3EB0ABCDEF123456', '[email protected]')

Update Presence

// set yourself as online / offline globally
client.setOnline(true)
client.setOnline(false)

// show typing or recording in a specific chat
client.setChatPresence('[email protected]', 'composing')   // typing
client.setChatPresence('[email protected]', 'recording')   // recording audio
client.setChatPresence('[email protected]', 'paused')      // stopped

Modifying Chats

Archive / Unarchive a Chat

client.archiveChat('[email protected]')
client.unarchiveChat('[email protected]')

Mute / Unmute a Chat

await client.muteChat('[email protected]', 8 * 60 * 60 * 1000)  // mute for 8 hours (ms)
await client.muteChat('[email protected]', 0)                    // mute indefinitely
await client.unmuteChat('[email protected]')

Mark a Chat Read / Unread

await client.markChatRead('[email protected]')   // sends IQ to server
client.markChatUnread('[email protected]')       // local state only

Pin / Unpin a Chat

client.pinChat('[email protected]')
client.unpinChat('[email protected]')

Star / Unstar a Message

client.starMessage('MSGID123', '[email protected]')
client.unstarMessage('MSGID123', '[email protected]')

Disappearing Messages

| Duration | Seconds | |---|---| | Off | 0 | | 24 hours | 86 400 | | 7 days | 604 800 | | 90 days | 7 776 000 |

// set disappearing timer for a specific chat (DM or group)
await client.changeEphemeralTimer('[email protected]', 86400)
await client.changeEphemeralTimer('[email protected]', 604800)

// remove disappearing messages
await client.changeEphemeralTimer('[email protected]', 0)

User Queries

Check If a Number Has WhatsApp

const { checkNumberStatus } = require('whalibmob')

const result = await checkNumberStatus('919634847671')
// result.status: 'registered' | 'registered_blocked' | 'not_registered' | 'cooldown' | 'unknown'
console.log(result.status)

Check multiple numbers at once while connected:

const results = await client.hasWhatsapp(['919634847671', '12345678901'])
// returns array of JIDs that have WhatsApp

Fetch Profile About

const about = await client.queryAbout('[email protected]')
console.log(about)

Fetch Profile Picture

const url = await client.queryPicture('[email protected]')
// also works for groups
const groupUrl = await client.queryPicture('[email protected]')

Subscribe to Presence

// triggers 'presence' events when the contact comes online or goes offline
client.subscribeToPresence('[email protected]')

client.on('presence', ({ from, available }) => {
  console.log(from, available ? 'online' : 'offline')
})

Change Profile

Change Display Name

client.changeName('My Bot')

Change About Text

await client.changeAbout('Available 24/7')

Change Profile Picture

Both methods accept a Buffer (use fs.readFileSync to load a file).

const fs = require('fs')

// change your own profile picture
await client.changeProfilePicture(fs.readFileSync('./avatar.jpg'))

// change a group's picture (you must be admin)
await client.changeGroupPicture('[email protected]', fs.readFileSync('./group.jpg'))

Privacy

Block / Unblock User

await client.blockContact('[email protected]')
await client.unblockContact('[email protected]')

Get Block List

const list = await client.queryBlockList()
console.log(list)   // [ '[email protected]', ... ]

Update Privacy Settings

// type:  'last_seen' | 'profile_picture' | 'status' | 'online' | 'read_receipts' | 'groups_add'
// value: 'all' | 'contacts' | 'contact_blacklist' | 'none' | 'match_last_seen'

await client.changePrivacySetting('last_seen',        'contacts')
await client.changePrivacySetting('profile_picture',  'contacts')
await client.changePrivacySetting('status',           'contacts')
await client.changePrivacySetting('online',           'match_last_seen')
await client.changePrivacySetting('read_receipts',    'none')
await client.changePrivacySetting('groups_add',       'contacts')

Update Default Disappearing Mode

// sets the default ephemeral timer for all new chats
await client.changeNewChatsEphemeralTimer(86400)   // 1 day
await client.changeNewChatsEphemeralTimer(0)       // off

Groups

Create a Group

Returns the same metadata object as getGroupMetadata (jid, subject, participants, etc.).

const group = await client.createGroup('My Group', [
  '[email protected]',
  '[email protected]'
])
console.log('created', group.jid)       // '[email protected]'
console.log('subject', group.subject)   // 'My Group'
console.log('members', group.participants.map(p => p.jid))

Add / Remove or Demote / Promote

const groupJid = '[email protected]'

await client.addGroupParticipants(groupJid,     ['[email protected]'])
await client.removeGroupParticipants(groupJid,  ['[email protected]'])
await client.promoteGroupParticipants(groupJid, ['[email protected]'])
await client.demoteGroupParticipants(groupJid,  ['[email protected]'])

Change Subject

await client.changeGroupSubject('[email protected]', 'New Group Name')

Change Description

await client.changeGroupDescription('[email protected]', 'This is the group description')

Change Settings

// setting: 'edit_group_info' | 'send_messages' | 'add_participants' | 'approve_participants'
// policy:  'admins' | 'all'

await client.changeGroupSetting('[email protected]', 'send_messages',   'admins')
await client.changeGroupSetting('[email protected]', 'edit_group_info', 'admins')
await client.changeGroupSetting('[email protected]', 'add_participants', 'all')

Leave a Group

await client.leaveGroup('[email protected]')

Get Invite Code

const link = await client.queryGroupInviteLink('[email protected]')
// e.g. 'https://chat.whatsapp.com/AbCdEfGhIjK'
console.log(link)

Revoke Invite Code

await client.revokeGroupInvite('[email protected]')

Join Using Invitation Code

[!NOTE] Pass only the code portion — do not include https://chat.whatsapp.com/

const jid = await client.acceptGroupInvite('AbCdEfGhIjK')
console.log('joined', jid)

Query Invite Info from Link

Fetch a group's metadata from an invite code or full URL without joining the group. Useful for displaying a preview to the user before they confirm.

// bare code
const info = await client.queryGroupInviteInfo('AbCdEfGhIjKlMnOpQrStUv')

// or pass the full URL — the code is extracted automatically
const info = await client.queryGroupInviteInfo('https://chat.whatsapp.com/AbCdEfGhIjKlMnOpQrStUv')

console.log(info)
// {
//   jid:          '[email protected]',
//   subject:      'My Group',
//   creator:      '[email protected]',
//   creation:     1705315800,   // Unix timestamp
//   description:  'Group description here',
//   participants: [
//     { jid: '[email protected]', role: 'admin' },
//     { jid: '[email protected]',  role: 'member' }
//   ]
// }

Fetch All Groups

Returns an array of metadata objects for every group you are a member of. Each object has the same shape as getGroupMetadata.

const groups = await client.fetchAllGroups()
for (const g of groups) {
  console.log(g.jid, g.subject, g.participants.length + ' members')
}

Query Metadata

const meta = await client.getGroupMetadata('[email protected]')
// returns: { jid, subject, creation, creator, subjectTime, subjectBy,
//            description, ephemeral, onlyAdminsSend, onlyAdminsEdit, participants[] }
console.log(meta.subject, meta.participants.length + ' members')

Get Request Join List

const pending = await client.queryGroupPendingParticipants('[email protected]')
console.log(pending)

Approve / Reject Request Join

The second parameter is a boolean: true to approve, false to reject.

// approve join requests
await client.approveGroupParticipants('[email protected]', true, [
  '[email protected]'
])

// reject join requests
await client.approveGroupParticipants('[email protected]', false, [
  '[email protected]'
])

Toggle Ephemeral in Group

await client.changeEphemeralTimer('[email protected]', 86400)  // 1 day
await client.changeEphemeralTimer('[email protected]', 0)      // off

Communities

WhatsApp Communities are a superset of groups — a parent container that can hold multiple linked sub-groups plus an automatic general-chat group.

Create a Community

const community = await client.createCommunity('My Community', 'A place for discussion')
// community.jid  — e.g. [email protected]

Deactivate / Delete a Community

await client.deactivateCommunity('[email protected]')

Link Groups into a Community

const linked = await client.linkGroupsToCommunity(
  '[email protected]',          // community JID
  ['[email protected]',         // group JIDs to link
   '[email protected]']
)

Unlink a Group from a Community

await client.unlinkGroupFromCommunity(
  '[email protected]',   // community JID
  '[email protected]'    // group JID
)

Newsletters (Channels)

Newsletters are one-to-many broadcast channels. Only the owner can post; anyone can subscribe.

Create a Newsletter

const nl = await client.createNewsletter('Tech News', 'Daily updates on tech')
// nl.jid — e.g. 120363000000000004@newsletter

Join / Leave a Newsletter

await client.joinNewsletter('120363000000000004@newsletter')
await client.leaveNewsletter('120363000000000004@newsletter')

Query Newsletter Metadata

const meta = await client.queryNewsletterMetadata('120363000000000004@newsletter')
// { jid, name, description, subscriberCount }

Update Newsletter Description

await client.changeNewsletterDescription('120363000000000004@newsletter', 'New description here')

Post a Text Update to Your Newsletter

await client.sendNewsletterText('120363000000000004@newsletter', 'Breaking: WhatsApp adds polls!')

Business Profile

Query the public business profile of any WhatsApp Business account:

const bp = await client.queryBusinessProfile('[email protected]')
if (bp) {
  console.log(bp.category)     // e.g. "Software & IT Services"
  console.log(bp.email)        // business email (if set)
  console.log(bp.website)      // business website (if set)
  console.log(bp.address)      // physical address (if set)
  console.log(bp.description)  // business description (if set)
}
// Returns null if the number is not a WhatsApp Business account

CLI — Getting Started

Install the CLI

Install whalibmob globally to get the wa command available from anywhere on your system:

npm install -g whalibmob

Verify the installation:

wa version

First-Time Setup: Register a Number

Registration is a one-time process. You need a phone number that can receive an SMS or voice call. Use a dedicated number — do not use a number already active on a real WhatsApp device.

Step 1 — request a verification code

# via SMS (default)
wa registration --request-code 919634847671

# via voice call
wa registration --request-code 919634847671 --method voice

# via an old WhatsApp account
wa registration --request-code 919634847671 --method wa_old

The CLI sends the code request, prints the result, and then stays open in the interactive shell. You will see:

requesting sms code for +919634847671...
  status  sent
  now run: wa registration --register 919634847671 --code <code>

staying in shell — use /reg confirm 919634847671 <code> to complete
wa>

Step 2 — confirm the code you received

Either run the one-shot command:

wa registration --register 919634847671 --code 123456

Or type it directly in the shell that stayed open:

wa> /reg confirm 919634847671 123456

On success you will see:

registered  session saved to /home/user/.waSession/919634847671.json
now run: /connect 919634847671

Check if a number already has WhatsApp

wa registration --check 919634847671

Output:

checking +919634847671...
  status  registered

Possible statuses: registered · registered_blocked · not_registered · cooldown · unknown

CLI Connect

After registering, connect with:

wa connect 919634847671

The shell opens with a persistent prompt:

connecting to +919634847671...
connected as +919634847671
wa +919634847671>

[!TIP] The shell never exits on its own. It stays open until you type /quit or press Ctrl+C. This is true for every command — registration, connection, sending messages — everything.

Use a custom session directory with --session:

wa connect 919634847671 --session /data/my-sessions

Listen Mode

Connect and print all incoming events to the terminal. The process stays alive indefinitely until you press Ctrl+C:

wa listen 919634847671

Output as messages arrive:

connected  listening on +919634847671  (Ctrl+C to stop)
  ────────────────────────────────────────────────────────
  time                    2025-03-13 10:00:05
  from                    [email protected]
  id                      3EB0ABCDEF123456
  text                    Hello there!

CLI — Interactive Shell Commands

After running wa connect <phone>, every feature of the library is available as a /command. Type /help at any time to see all commands.

[!NOTE] JIDs can be written as plain phone numbers (e.g. 919634847671) — the shell automatically appends @s.whatsapp.net. For groups, use the full @g.us JID.

Messaging Commands

Send Text

wa> /send [email protected] Hello, how are you?
sent  3EB0ABCDEF123456

# to a group
wa> /send [email protected] Hello everyone!

Send Image

wa> /image [email protected] ./photo.jpg
wa> /image [email protected] ./photo.jpg Look at this!

The second argument is the file path. The optional third argument is the caption.

Send Video

wa> /video [email protected] ./clip.mp4
wa> /video [email protected] ./clip.mp4 Watch this

Send Audio

Sends the file as a regular audio attachment:

wa> /audio [email protected] ./song.mp3

Send Voice Note

Sends the file as a push-to-talk voice note with waveform:

wa> /ptt [email protected] ./voice.ogg

Send Document

wa> /doc [email protected] ./report.pdf
wa> /doc [email protected] ./report.pdf "Q1 Report.pdf"

The optional third argument overrides the displayed filename.

Send Sticker

The file must be in WebP format:

wa> /sticker [email protected] ./sticker.webp

Send Poll (CLI)

Separate the question from the options using |. At least two options are required. Optionally append selectable=N to limit how many options a voter may choose (0 = any):

# single-choice poll (selectable=1)
wa> /poll [email protected] Best language? | JavaScript | Python | Rust | selectable=1

# unlimited-choice poll (default)
wa> /poll [email protected] Pick your favourites | Red | Green | Blue

React to a Message

wa> /react [email protected] 3EB0ABCDEF123456 👍

# remove a reaction — pass a space or empty string
wa> /react [email protected] 3EB0ABCDEF123456 " "

The message ID is shown in the incoming message display as id.

Edit a Message

[!NOTE] Editing is only possible within 15 minutes of the original send.

wa> /edit [email protected] 3EB0ABCDEF123456 Corrected text here

Delete a Message

# delete for yourself only
wa> /delete [email protected] 3EB0ABCDEF123456

# delete for everyone (revoke)
wa> /delete [email protected] 3EB0ABCDEF123456 all

Post a Status / Story

Posts a text Status visible to your contacts:

wa> /status Good morning everyone!

Forward a Message

Sends a message with the forwarded flag set:

wa> /forward [email protected] This message was forwarded

Reply to a Message (CLI)

Quote and reply to a specific message. You need the message ID (shown as id: in the receive log) and the sender's JID.

# DM — senderJid is the same as the chat JID
wa> /reply [email protected] 3EB0XXXXXXXX [email protected] Got it, thanks!

# Group — senderJid is the member who sent the original message
wa> /reply [email protected] 3EB0XXXXXXXX [email protected] Agreed!

The message ID is printed when a message arrives:

  id                    3EB0C5BA7XXXXXXXX

Send Location (CLI)

Send a GPS location pin. Latitude and longitude are required; name and address (separated by |) are optional:

# lat/lon only
wa> /location [email protected] 48.8566 2.3522

# with name
wa> /location [email protected] 48.8566 2.3522 Eiffel Tower

# with name and address (separate with |)
wa> /location [email protected] 48.8566 2.3522 Eiffel Tower | Champ de Mars, Paris

# to a group
wa> /location [email protected] 51.5074 -0.1278 London

Send Contact / vCard (CLI)

Send a contact card. The vCard string must follow the vCard v3 format. Wrap it in quotes in the shell:

wa> /vcard [email protected] "Alice Smith" "BEGIN:VCARD\nVERSION:3.0\nFN:Alice Smith\nTEL;TYPE=CELL:+919634847671\nEND:VCARD"

For multi-line vCards it is easiest to store the string in a shell variable:

VCARD="BEGIN:VCARD
VERSION:3.0
FN:Alice Smith
TEL;TYPE=CELL:+919634847671
EMAIL:[email protected]
END:VCARD"

wa> /vcard [email protected] "Alice Smith" "$VCARD"

Presence Commands

Set Online / Offline

wa> /online
wa> /offline

Typing and Recording Indicators

# show "typing…" in a chat
wa> /typing [email protected]

# show "recording audio…" in a chat
wa> /recording [email protected]

# stop the indicator
wa> /stop [email protected]

Subscribe to a Contact's Presence

Subscribes to online/offline events for a contact. The shell will print presence updates as they arrive:

wa> /subscribe [email protected]
subscribed to [email protected]

# when they come online:
  presence  [email protected]  online

Profile Commands

CLI Change Display Name

wa> /name My Bot Name
name updated

CLI Change About Text

wa> /about Available 24/7 for support
about updated

CLI Change Profile Picture

Reads the image from disk and uploads it as your profile picture. Supported formats: JPEG, PNG.

wa> /photo ./avatar.jpg
profile picture updated

CLI Change Privacy Settings

wa> /privacy last_seen contacts
wa> /privacy profile_picture contacts
wa> /privacy status contacts
wa> /privacy online match_last_seen
wa> /privacy read_receipts none
wa> /privacy groups_add contacts

Available types: last_seen · profile_picture · status · online · read_receipts · groups_add

Available values: all · contacts · contact_blacklist · none · match_last_seen


Contact Commands

Check Who Has WhatsApp

Checks multiple phone numbers (plain digits, no +) and lists which ones are registered on WhatsApp:

wa> /whatsapp 919634847671 12345678901
  has whatsapp (1)
    [email protected]
  not found (1)
    12345678901

Get Profile Picture URL

Returns the CDN URL for a contact's or group's profile picture:

wa> /picture [email protected]
  https://mmg.whatsapp.net/v/...

wa> /picture [email protected]
  https://mmg.whatsapp.net/v/...

Get Contact About Text

Fetches the bio / about text for a contact:

wa> /contact about [email protected]
  Available 24/7

Chat Management Commands

Mark Read / Unread

wa> /read   [email protected]
wa> /unread [email protected]

Mute / Unmute

# mute for 60 minutes
wa> /mute [email protected] 60

# mute indefinitely
wa> /mute [email protected]

# unmute
wa> /unmute [email protected]

Pin / Unpin

wa> /pin   [email protected]
wa> /unpin [email protected]

Archive / Unarchive

wa> /archive   [email protected]
wa> /unarchive [email protected]

Star / Unstar a Message (CLI)

wa> /star   [email protected] 3EB0ABCDEF123456
wa> /unstar [email protected] 3EB0ABCDEF123456

CLI Disappearing Messages

| Duration | Seconds | |---|---| | Off | 0 | | 24 hours | 86400 | | 7 days | 604800 | | 90 days | 7776000 |

# set 1-day timer on a DM
wa> /ephemeral [email protected] 86400

# set 1-week timer on a group
wa> /ephemeral [email protected] 604800

# turn off
wa> /ephemeral [email protected] 0

Default Disappearing Timer

Sets the global default ephemeral timer applied to all new chats:

wa> /ephemeral-default 86400
default ephemeral set  86400

# turn off
wa> /ephemeral-default 0
default ephemeral set  0

Accepts the same values as /ephemeral: 0, 86400, 604800, 7776000.

Block / Unblock

wa> /block   [email protected]
blocked  [email protected]

wa> /unblock [email protected]
unblocked  [email protected]

Show Block List

wa> /blocklist
  blocked (2)
    [email protected]
    [email protected]

Group Commands

CLI Create a Group

wa> /group create MyGroup [email protected] [email protected]
creating group...
created  [email protected]
  subject  MyGroup
  members  [email protected], [email protected]

CLI Leave a Group

wa> /group leave [email protected]
left  [email protected]

Add / Remove Participants

# add participants
wa> /group add [email protected] [email protected]

# remove participants
wa> /group remove [email protected] [email protected]

Multiple participants can be listed, separated by spaces.

Promote / Demote Admins

# promote to admin
wa> /group promote [email protected] [email protected]

# demote from admin
wa> /group demote [email protected] [email protected]

Change Group Name

wa> /group subject [email protected] New Group Name
subject updated

Change Group Description

wa> /group desc [email protected] This is the group for project updates
description updated

Change Group Picture

Reads the image from disk and sets it as the group's profile picture. You must be an admin.

wa> /group photo [email protected] ./group-logo.jpg
group picture updated

Get Invite Link

wa> /group invite [email protected]
  https://chat.whatsapp.com/AbCdEfGhIjKlMnOpQrStUv

Revoke Invite Link

Invalidates the current invite link and generates a new one:

wa> /group revoke [email protected]
invite link revoked

Join a Group by Invite Code

Pass only the code part — do not include https://chat.whatsapp.com/:

wa> /group join AbCdEfGhIjKlMnOpQrStUv
joined  [email protected]

Query Group Invite Info

Preview a group's metadata from an invite link before joining. Accepts the bare code or the full URL:

wa> /group invite-info AbCdEfGhIjKlMnOpQrStUv
  ────────────────────────────────────────────────────────
  jid                   [email protected]
  subject               My Group
  creator               [email protected]
  created               2024-01-15 10:30:00
  description           Group description here
  participants          (3)
    [email protected]  [admin]
    [email protected]
    [email protected]
  ────────────────────────────────────────────────────────

You can also pass the full link:

wa> /group invite-info "https://chat.whatsapp.com/AbCdEfGhIjKlMnOpQrStUv"

Query Group Metadata

wa> /group meta [email protected]
  jid              [email protected]
  subject          My Group
  description      Group description here
  creator          [email protected]
  created          2024-01-15 10:30:00
  participants     3
  onlyAdminsSend   false
  onlyAdminsEdit   true
  ephemeral        0

List All Groups

Fetches all groups you are a member of and prints a numbered list:

wa> /groups
  groups (3)
    1  [email protected]  My Project Group  (5 members)
    2  [email protected]  Family Chat        (12 members)
    3  [email protected]  Friends            (8 members)

List Group Participants

Lists all participants of a group with their roles:

wa> /group participants [email protected]
  participants (3)
    [email protected]  [admin]
    [email protected]
    [email protected]

Pending Join Requests

Lists users who have requested to join a group (only visible when approve_participants is enabled):

wa> /group pending [email protected]
  pending (2)
    [email protected]
    [email protected]

Approve / Reject Join Requests

# approve one or more pending members
wa> /group approve [email protected] [email protected]
approved  [email protected]

# reject one or more pending members
wa> /group reject [email protected] [email protected]
rejected  [email protected]

Multiple JIDs can be listed, separated by spaces.

Group Settings

Controls who can send messages, edit group info, add participants, or requires approval to join:

# only admins can send messages
wa> /group settings [email protected] send_messages admins

# everyone can send messages
wa> /group settings [email protected] send_messages all

# only admins can edit group info
wa> /group settings [email protected] edit_group_info admins

# only admins can add participants
wa> /group settings [email protected] add_participants admins

# require admin approval for join requests
wa> /group settings [email protected] approve_participants admins

Community Commands (CLI)

Communities group multiple linked groups under one umbrella. Only the community creator can link / unlink groups or deactivate the community.

# create a community (description is optional)
wa> /community create "Dev Squad" "Our developer community"

# link an existing group into the community
wa> /community link  [email protected] [email protected]

# unlink a group from the community
wa> /community unlink [email protected] [email protected]

# permanently deactivate (delete) a community
wa> /community deactivate [email protected]

Newsletter / Channel Commands

Newsletters are one-to-many broadcast channels. Only the owner can post; anyone can subscribe.

# create a new channel
wa> /newsletter create Tech News Daily tips about technology

# subscribe to a channel
wa> /newsletter join 120363000000000004@newsletter

# unsubscribe from a channel
wa> /newsletter leave 120363000000000004@newsletter

# query channel metadata (name, description, subscriber count)
wa> /newsletter info 120363000000000004@newsletter
  ────────────────────────────────────────────────────────
  jid           120363000000000004@newsletter
  name          Tech News
  description   Daily tips about technology
  subscribers   1234
  ────────────────────────────────────────────────────────

# update the channel description (you must be the owner)
wa> /newsletter desc 120363000000000004@newsletter New description here

# post a text update to your channel (you must be the owner)
wa> /newsletter post 120363000000000004@newsletter Breaking: WhatsApp adds polls!

Business Profile Command (CLI)

Query the public business profile of any WhatsApp Business account:

wa> /biz [email protected]
  ────────────────────────────────────────────────────────
  jid           [email protected]
  category      Software & IT Services
  email         [email protected]
  website       https://example.com
  address       123 Main St
  description   We build software
  ────────────────────────────────────────────────────────

Returns a message if the number is not a WhatsApp Business account.


Registration Commands (in-shell)

These commands work from within the shell — useful when you need to register a second number without closing the current session:

# check if a phone number has WhatsApp
wa> /reg check 919634847671

# request a verification code
wa> /reg code 919634847671
wa> /reg code 919634847671 voice
wa> /reg code 919634847671 wa_old

# confirm the code received
wa> /reg confirm 919634847671 123456
registered  session saved to /home/user/.waSession/919634847671.json
now run: /connect 919634847671

Connection Commands (in-shell)

# connect to a number (while already in the shell)
wa> /connect 919634847671

# disconnect
wa> /disconnect

# force a reconnection
wa> /reconnect

# show current session info
wa> /session
  phone                   919634847671
  name                    My Bot
  session                 /home/user/.waSession

# show all available commands
wa> /help

# disconnect and exit the shell
wa> /quit

Full Command Reference Table

| Command | Description | |---|---| | Messaging | | | /send <jid> <text> | Send a text message | | /image <jid> <file> [caption] | Send an image | | /video <jid> <file> [caption] | Send a video | | /audio <jid> <file> | Send an audio file | | /ptt <jid> <file> | Send a voice note (push-to-talk) | | /doc <jid> <file> [name] | Send a document | | /sticker <jid> <file> | Send a sticker (.webp) | | /poll <jid> <question> \| <opt1> \| <opt2> [selectable=N] | Send a poll | | /react <jid> <msgId> <emoji> | React to a message | | /edit <jid> <msgId> <text> | Edit a sent message | | /delete <jid> <msgId> [all] | Delete a message (add all for everyone) | | /status <text> | Post a Status / Story | | /forward <jid> <text> | Send with forwarded flag | | /reply <jid> <msgId> <senderJid> <text> | Reply quoting a specific message | | /location <jid> <lat> <lon> [name] [| address] | Send a GPS location pin | | /vcard <jid> <displayName> <vcard> | Send a contact card (vCard v3) | | Presence | | | /online | Set yourself as online | | /offline | Set yourself as offline | | /typing <jid> | Show typing indicator in a chat | | /recording <jid> | Show recording audio indicator | | /stop <jid> | Stop typing / recording | | /subscribe <jid> | Subscribe to a contact's presence | | Profile | | | /name <text> | Change your display name | | /about <text> | Change your bio / about text | | /photo <file> | Change your profile picture | | /privacy <type> <value> | Change a privacy setting | | Contacts | | | /whatsapp <phone...> | Check which numbers have WhatsApp | | /picture <jid> | Get profile picture CDN URL | | /contact about <jid> | Get bio / about text of a contact | | Chat Management | | | /read <jid> | Mark chat as read | | /unread <jid> | Mark chat as unread | | /mute <jid> [minutes] | Mute a chat (indefinitely if no minutes given) | | /unmute <jid> | Unmute a chat | | /pin <jid> | Pin a chat | | /unpin <jid> | Unpin a chat | | /archive <jid> | Archive a chat | | /unarchive <jid> | Unarchive a chat | | /star <jid> <msgId> | Star a message | | /unstar <jid> <msgId> | Unstar a message | | /ephemeral <jid> <seconds> | Set disappearing messages timer for a chat | | /ephemeral-default <seconds> | Set global default ephemeral timer for new chats | | /block <jid> | Block a contact | | /unblock <jid> | Unblock a contact | | /blocklist | Show all blocked contacts | | Groups | | | /group create <name> <jid...> | Create a group | | /group leave <jid> | Leave a group | | /group add <jid> <member...> | Add participants | | /group remove <jid> <member...> | Remove participants | | /group promote <jid> <member...> | Promote to admin | | /group demote <jid> <member...> | Demote from admin | | /group subject <jid> <name> | Rename group | | /group desc <jid> <text> | Change group description | | /group photo <jid> <file> | Change group picture | | /group invite <jid> | Get invite link | | /group revoke <jid> | Revoke invite link | | /group join <code> | Join group by invite code | | /group invite-info <code\|url> | Preview group metadata from an invite link (without joining) | | /groups | List all groups you are a member of | | /group meta <jid> | Query group metadata | | /group participants <jid> | List group participants with roles | | /group pending <jid> | List pending join requests | | /group approve <jid> <member...> | Approve pending join requests | | /group reject <jid> <member...> | Reject pending join requests | | /group settings <jid> <setting> <policy> | Change group setting | | Community | | | /community create <subject> [description] | Create a community | | /community deactivate <communityJid> | Permanently delete a community | | /community link <communityJid> <groupJid> | Link a group into a community | | /community unlink <communityJid> <groupJid> | Unlink a group from a community | | Newsletter / Channel | | | /newsletter create <name> [description] | Create a newsletter channel | | /newsletter join <jid> | Subscribe to a channel | | /newsletter leave <jid> | Unsubscribe from a channel | | /newsletter info <jid> | Query channel metadata | | /newsletter desc <jid> <text> | Update channel description | | /newsletter post <jid> <text> | Post a text update to your channel | | Business | | | /biz <phone\|jid> | Query business profile of a WhatsApp Business account | | Registration | | | /reg check <phone> | Check if number has WhatsApp | | /reg code <phone> [method] | Request verification code | | /reg confirm <phone> <code> | Complete registration | | Connection | | | /connect <phone> | Connect to WhatsApp | | /disconnect | Disconnect current session | | /reconnect | Force reconnection | | /session | Show session info | | /help | Show all commands | | /quit / /exit | Disconnect and exit |


WhatsApp IDs

  • Individual contacts: [countrycode][number]@s.whatsapp.net
  • Groups: [groupid]@g.us
  • Status broadcast: status@broadcast
  • Broadcast lists: [timestamp]@broadcast

[!NOTE] Phone numbers must include the country code without the + prefix.

Transport

whalibmob uses Noise_XX_25519_AESGCM_SHA256 over TCP to g.whatsapp.net:443:

  1. Client sends ClientHello with an ephemeral X25519 public key.
  2. Server replies ServerHello (ephemeral + encrypted static + payload).
  3. Three DH steps (EE, SE, SS) derive the final session keys.
  4. Client sends ClientFinish (encrypted static + encrypted payload).
  5. The library waits for a <success> stanza before emitting connected, or <failure> for auth_failure.

Automatic reconnection uses exponential backoff:

| Attempt | Delay | |---|---| | 1 | 1 s | | 2 | 2 s | | 3 | 4 s | | 4 | 8 s | | 5 | 15 s | | 6+ | 30 s |

Media Encryption

Sending (upload flow)

  1. A random 32-byte media key is generated.
  2. HKDF-SHA256 expands it into IV (16 bytes), cipher key (32 bytes), and MAC key (32 bytes).
  3. The file is encrypted with AES-256-CBC.
  4. A 10-byte HMAC-SHA256 MAC is appended to the ciphertext.
  5. The encrypted blob is uploaded to the WhatsApp CDN.
  6. The media key and CDN URL are embedded in the Signal-encrypted message envelope sent to the recipient.

Receiving (download + decrypt flow)

When you receive a media message, msg.decoded contains:

  • url — the HTTPS CDN link to download the encrypted blob
  • mediaKey — the 32-byte key (as a Buffer) needed to decrypt it
  • directPath — CDN path (fallback if full URL is unavailable)

The decryption process mirrors the upload:

| Step | Operation | |---|---| | 1 | Download encrypted blob from CDN URL | | 2 | HKDF-SHA256(mediaKey, "", "WhatsApp <Type> Keys", 112) → expanded key material | | 3 | Split: [0:16] = IV · [16:48] = AES cipher key · [48:80] = HMAC key | | 4 | Verify: HMAC-SHA256(macKey, IV ∥ ciphertext)[:10] must equal last 10 bytes of blob | | 5 | Decrypt: AES-256-CBC(cipherKey, IV, ciphertext) — strip last 10 bytes first |

HKDF info strings:

| Media type | Info string | |---|---| | image / sticker | WhatsApp Image Keys | | video | WhatsApp Video Keys | | audio / voice | WhatsApp Audio Keys | | document | WhatsApp Document Keys |

See the Receiving Media section for a complete working code example.

Device Emulation

whalibmob emulates an iPhone when communicating with WhatsApp servers. The device profile controls the User-Agent header, the Noise Protocol platform field, and the token computation.

When no WA_DEVICE is set, the library picks a random iPhone model from the built-in profile list for each new session. This provides natural device diversity.

Configuration is done entirely through environment variables — no code changes required. Copy .env.example to .env in your project root and set the variables you need.

Device Quick Start

Emulate an iPhone 16 Pro:

WA_DEVICE=iphone16pro node your-app.js

Or put the variables in a .env file. When using the CLI (wa command) the file is loaded automatically. When using the library directly, load it before require('whalibmob'):

require('dotenv').config()          // must be first
const { WhalibmobClient } = require('whalibmob')
WA_DEVICE=iphone16pro

iPhone Profiles

Available values for WA_DEVICE:

| Profile key | Device | iOS version | |---|---|---| | iphone16promax | iPhone 16 Pro Max | 18.3.2 | | iphone16pro | iPhone 16 Pro | 18.3.2 | | iphone16plus | iPhone 16 Plus | 18.3.2 | | iphone16 | iPhone 16 | 18.3.2 | | iphone15promax | iPhone 15 Pro Max | 18.3.2 | | iphone15pro | iPhone 15 Pro | 18.3.2 | | iphone15plus | iPhone 15 Plus | 18.3.2 | | iphone15 | iPhone 15 | 18.3.2 | | iphone14promax | iPhone 14 Pro Max | 18.3.2 | | iphone14pro | iPhone 14 Pro | 18.3.2 | | iphone14plus | iPhone 14 Plus | 17.7.5 | | iphone14 | iPhone 14 | 17.7.5 | | iphone13pro | iPhone 13 Pro | 17.7.5 | | iphone13 | iPhone 13 | 17.7.5 | | iphone12pro | iPhone 12 Pro | 17.7.5 | | iphone12 | iPhone 12 | 17.7.5 | | iphonese3 | iPhone SE (3rd gen) | 17.7.5 | | iphone11pro | iPhone 11 Pro | 17.7.5 | | iphone11 | iPhone 11 | 17.7.5 | | iphonexs | iPhone Xs | 16.7.11 |

User-Agent format: WhatsApp/<version> iOS/<osVersion> Device/<model>

The WhatsApp version is fetched automatically from the Apple App Store (iTunes API) every time you register, always using the latest published version. The result is cached for 6 hours so repeated calls within a session are fast. If all App Store sources fail (no internet, rate limit, etc.), the built-in IOS_VERSION_FALLBACK constant is used as a last resort. Three iTu