Advanced Features

Business tools, communities, labels, Signal, USync, media download, proxy, and utility functions.

Business Profile

getBusinessProfile

Fetch another user's business profile.

const profile = await sock.getBusinessProfile('628xxx@s.whatsapp.net')
console.log(profile.description, profile.email, profile.website, profile.address)
console.log(profile.category, profile.business_hours)

updateBussinesProfile

Update your own business profile.

await sock.updateBussinesProfile({
  address: '123 Main St',
  email: 'store@example.com',
  description: 'Trusted online store',
  websites: ['https://example.com'],
  hours: {
    timezone: 'America/New_York',
    days: [
      { day: 'mon', mode: 'specific_hours', openTimeInMinutes: 480, closeTimeInMinutes: 1020 },
      { day: 'tue', mode: 'open_24h' },
      { day: 'sun', mode: 'closed' },
    ],
  },
})

updateCoverPhoto & removeCoverPhoto

const photoId = await sock.updateCoverPhoto({ url: './cover.jpg' })
await sock.removeCoverPhoto(photoId)

Catalog & Products

getCatalog

List products from a catalog.

const catalog = await sock.getCatalog({ jid: '628xxx@s.whatsapp.net', limit: 20 })
console.log(catalog.products)
console.log(catalog.cursor)  // for pagination
const nextPage = await sock.getCatalog({ jid, cursor: catalog.cursor })

getCollections

const collections = await sock.getCollections('628xxx@s.whatsapp.net', 20)

productCreate / productUpdate / productDelete

// Create a new product
const product = await sock.productCreate({
  name: 'Premium T-Shirt',
  description: '100% cotton T-shirt',
  images: [{ url: 'https://example.com/tshirt.jpg' }],
  currency: 'IDR',
  price: 5000000,        // Rp50.000 (price in cents x 100)
  originCountryCode: 'ID',
  isHidden: false,
})

// Update product
await sock.productUpdate(productId, {
  name: 'Premium T-Shirt V2',
  price: 7500000,
})

// Delete product
const result = await sock.productDelete([productId])
console.log('Deleted:', result.deleted)

// Order details
const order = await sock.getOrderDetails(orderId, tokenBase64)

Communities

Community features — large groups with sub-groups.

Creating & Managing Communities

// Create community
const community = await sock.communityCreate('Community Name', 'Description')

// Create group inside community
const group = await sock.communityCreateGroup('Sub Group', ['628xxx@s.whatsapp.net'], '123456@g.us')

// Get community metadata
const meta = await sock.communityMetadata('123456@g.us')

// Link / unlink group to community
await sock.communityLinkGroup('subgroup@g.us', 'community@g.us')
await sock.communityUnlinkGroup('subgroup@g.us', 'community@g.us')

// Fetch all linked groups
const linked = await sock.communityFetchLinkedGroups('community@g.us')
console.log(linked.linkedGroups)

// Leave community
await sock.communityLeave('123456@g.us')

// Update name & description
await sock.communityUpdateSubject('123456@g.us', 'New Name')
await sock.communityUpdateDescription('123456@g.us', 'New description')

Participants & Invites

// Add / remove members
await sock.communityParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'add')
await sock.communityParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'remove')
await sock.communityParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'promote')

// Invite code
const inviteCode = await sock.communityInviteCode('123@g.us')
await sock.communityRevokeInvite('123@g.us')
const jid = await sock.communityAcceptInvite('invitecode')
const info = await sock.communityGetInviteInfo('invitecode')

// Join requests
const requests = await sock.communityRequestParticipantsList('123@g.us')
await sock.communityRequestParticipantsUpdate('123@g.us', ['628xxx@s.whatsapp.net'], 'approve')

Community Settings

await sock.communitySettingUpdate('123@g.us', 'announcement')
await sock.communityToggleEphemeral('123@g.us', 86400)
await sock.communityJoinApprovalMode('123@g.us', 'on')
await sock.communityMemberAddMode('123@g.us', 'admin_add')

// Fetch all communities
const all = await sock.communityFetchAllParticipating()

Labels

Labels for chats and messages.

// Add label
await sock.addLabel('123@g.us', { name: 'Important', labelColor: 1 })

// Label chat
await sock.addChatLabel('628xxx@s.whatsapp.net', 'label_id')
await sock.removeChatLabel('628xxx@s.whatsapp.net', 'label_id')

// Label specific message
await sock.addMessageLabel('628xxx@s.whatsapp.net', 'message_id', 'label_id')
await sock.removeMessageLabel('628xxx@s.whatsapp.net', 'message_id', 'label_id')

Signal (Encryption)

Direct access to the Signal repository for debugging or manual operations.

decryptMessage

Decrypt a 1-on-1 message.

const decrypted = await sock.signalRepository.decryptMessage({
  jid: '628xxx@s.whatsapp.net',
  type: 'pkmsg',  // or 'msg'
  ciphertext: buffer,
})

decryptGroupMessage

Decrypt a group message (sender key).

const decrypted = await sock.signalRepository.decryptGroupMessage({
  group: '123@g.us',
  data: cipherBuffer,
  meId: sock.user?.id,
})

encryptMessage / encryptGroupMessage

// Encrypt 1-on-1
const { type, ciphertext } = await sock.signalRepository.encryptMessage({
  jid: '628xxx@s.whatsapp.net',
  data: plaintextBuffer,
})

// Encrypt group
const result = await sock.signalRepository.encryptGroupMessage({
  group: '123@g.us',
  data: plaintextBuffer,
  meId: sock.user?.id,
})

Signal Events

The library automatically handles session management, pre-key upload, etc. You can listen to the creds.update event to see key changes.


USync

WhatsApp query protocol for fetching user data (devices, status, LID mapping, etc.).

executeUSyncQuery

import { USyncQuery, USyncUser } from '@mataram/wa'

const query = new USyncQuery()
  .withContext('business')
  .withDeviceProtocol()
  .withContactProtocol()
  .withLIDProtocol()
  .withStatusProtocol()
  .withDisappearingModeProtocol()
  .withUser(new USyncUser().withId('628xxx@s.whatsapp.net'))
  .withUser(new USyncUser().withPhone('+6281234567890'))

const result = await sock.executeUSyncQuery(query)
console.log(result.list)
// Each item: { id, device, contact, lid, status, disappearingMode }

USyncUser

MethodDescription
withId(jid)Look up user by JID
withPhone(phone)Look up user by phone number (format: +628xxx)

USyncQuery Protocols

ProtocolMethodDescription
Device.withDeviceProtocol()Get user device list
Contact.withContactProtocol()Check if number is registered on WA
LID.withLIDProtocol()Get LID to PN mapping
Status.withStatusProtocol()Get user status/about
Disappearing Mode.withDisappearingModeProtocol()Get disappearing mode settings

onWhatsApp

Check if phone numbers are registered on WhatsApp. Simpler than manual USync.

const results = await sock.onWhatsApp('6281234567890', '6289876543210')
for (const { jid, exists } of results) {
  console.log(jid, exists ? 'Registered' : 'Not registered')
}

Call Links

Create audio/video call links.

// Create video call link
const token = await sock.createCallLink('video')
console.log('Call link:', `https://call.whatsapp.com/video/${token}`)

// With schedule
const token2 = await sock.createCallLink('audio', { startTime: new Date('2025-07-01T10:00:00').getTime() })

Contact Management

// Add / edit contact
await sock.addOrEditContact('628xxx@s.whatsapp.net', {
  firstName: 'John',
  fullName: 'John Doe',
  phoneNumber: [{ phone: '+6281234567890' }],
})

// Remove contact
await sock.removeContact('628xxx@s.whatsapp.net')

// Block / unblock user
await sock.updateBlockStatus('628xxx@s.whatsapp.net', 'block')
await sock.updateBlockStatus('628xxx@s.whatsapp.net', 'unblock')

// Fetch blocklist
const blocklist = await sock.fetchBlocklist()

Quick Reply

// Add / edit quick reply
await sock.addOrEditQuickReply({
  name: '.help',
  shortcut: '.help',
  message: 'Available commands:\n!menu - Menu\n!about - About',
})

// Remove quick reply
await sock.removeQuickReply(timestamp)

Media Download

Download media from messages (image, video, audio, document).

downloadContentFromMessage

Low-level function. Download & decrypt stream directly from WA servers.

import { downloadContentFromMessage, toBuffer } from '@mataram/wa'

const downloadMedia = async (msg) => {
  const content = msg.message?.imageMessage // or videoMessage, audioMessage, documentMessage
  if (!content) return

  const stream = await downloadContentFromMessage(content, 'image')
  const buffer = await toBuffer(stream)

  // Save to file
  fs.writeFileSync('./download.jpg', buffer)
}

// Supported media types
// 'image' | 'video' | 'audio' | 'document' | 'sticker'

downloadMediaMessage

High-level function. Download directly from a WAMessage object.

import { downloadMediaMessage } from '@mataram/wa'

const buffer = await downloadMediaMessage(msg, 'buffer')  // get Buffer directly
const stream = await downloadMediaMessage(msg, 'stream')  // get Readable stream

Proxy

Use a proxy for WebSocket connection and fetch requests.

import { SocksProxyAgent } from 'socks-proxy-agent'

const agent = new SocksProxyAgent('socks5://127.0.0.1:1080')

const sock = makeWASocket({
  auth: state,
  agent,         // proxy for WebSocket
  fetchAgent: agent,  // proxy for upload/download media
})

HTTP/HTTPS proxy can also be used with HttpsProxyAgent from https-proxy-agent.


Utilities

fetchLatestVersion

Get the latest WA Web version.

const [version, isNew] = await fetchLatestVersion()
console.log('Version:', version.join('.'))

BufferJSON

JSON serializer/deserializer that supports Buffer. Used internally for saving creds.

import { BufferJSON } from '@mataram/wa'

const data = { buffer: Buffer.from('hello') }
const json = JSON.stringify(data, BufferJSON.replacer)
const parsed = JSON.parse(json, BufferJSON.reviver)

getContentType

Get the content type from a message.

import { getContentType } from '@mataram/wa'

const type = getContentType(msg.message)
// 'conversation' | 'imageMessage' | 'videoMessage' | 'extendedTextMessage' | 'pollCreationMessage' | ...

getDevice

Detect which device sent the message.

import { getDevice } from '@mataram/wa'

const device = getDevice(msg.key.id)
// 'ios' | 'android' | 'web' | 'unknown'

makeCacheableSignalKeyStore

Wrap key store with cache to avoid reading from disk every time.

import { makeCacheableSignalKeyStore } from '@mataram/wa'

const cachedKeys = makeCacheableSignalKeyStore(authState.keys)

delay

Simple sleep helper.

import { delay } from '@mataram/wa'

await delay(3000)  // wait 3 seconds

fetchStatus

const statusList = await sock.fetchStatus('628xxx@s.whatsapp.net', '628yyy@s.whatsapp.net')
for (const { id, status, setAt } of statusList) {
  console.log(id, status, setAt)
}

fetchDisappearingDuration

const result = await sock.fetchDisappearingDuration('628xxx@s.whatsapp.net')
console.log(result)

presenceSubscribe

Subscribe to a user's presence updates.

await sock.presenceSubscribe('628xxx@s.whatsapp.net')

getBotListV2

Get the list of available WA bots.

const bots = await sock.getBotListV2()
console.log(bots)

updateMemberLabel

Update a group member's label.

await sock.updateMemberLabel('123@g.us', 'member')

updateMediaMessage

Request a re-download for failed media (retry).

try {
  await sock.updateMediaMessage(msg)
  console.log('Media updated!')
} catch (err) {
  console.log('Failed to update media', err)
}

logout

Explicitly logout from WhatsApp.

await sock.logout()

Account State & Restrictions

WhatsApp has anti-spam systems that can restrict bot accounts. These functions let you check your standing.

Reachout Timelock

Checks if your account is temporarily restricted from reaching out to new contacts. Usually triggered by sending messages to too many new numbers in a short time.

fetchAccountReachoutTimelock

ReturnTypeDescription
isActivebooleanWhether the account is currently restricted
timeEnforcementEndsDate | undefinedWhen the restriction ends
enforcementTypeReachoutTimelockEnforcementTypeType of restriction
enforcementTypeMeaning
DEFAULTNormal, no restriction
WEB_COMPANION_ONLYOnly companion/web devices blocked, main phone OK
RESTRICT_ALL_COMPANIONSAll devices blocked from reaching new contacts
BIZ_QUALITYLow business quality score
BIZ_COMMERCE_VIOLATION_*Commerce violations (15 types: ALCOHOL, DRUGS, WEAPONS, etc.)
const timelock = await sock.fetchAccountReachoutTimelock()
if (timelock.isActive) {
  console.log('Account restricted:', timelock.enforcementType)
  console.log('Ends:', timelock.timeEnforcementEnds)
}

This is different from a ban. Usually temporary (24-72h). During restriction, avoid sending messages to new contacts — retrying only makes it worse. @mataram/wa automatically handles error 463 (SenderReachoutTimelocked) and fetches this status.

Message Capping

WhatsApp also enforces a new chat quota per cycle. Different from reachout timelock: this limits the count, not blocking entirely.

fetchNewChatMessageCap

FieldTypeDescription
capping_statusstringNONE / FIRST_WARNING / SECOND_WARNING / CAPPED
total_quotanumberTotal new chat quota in this cycle
used_quotanumberQuota already used
cycle_start_timestampstringUnix timestamp cycle started
cycle_end_timestampstringUnix timestamp cycle ends
ote_statusstringNOT_ELIGIBLE / ELIGIBLE / ACTIVE_IN_CURRENT_CYCLE / EXHAUSTED
mv_statusstringNOT_ELIGIBLE / NOT_ACTIVE / ACTIVE / ACTIVE_UPGRADE_AVAILABLE
const cap = await sock.fetchNewChatMessageCap()
console.log('Status:', cap.capping_status)
console.log('Quota:', cap.used_quota + '/' + cap.total_quota)

Rich Response

Send markdown-formatted messages rendered as native AI bot responses — tables, code blocks, syntax highlighting, and suggested prompts.

sendMessage with rich: true

Enable rich response by adding rich: true to the content object. @mataram/wa converts the markdown into a native interactive message that WhatsApp renders as an AI bot response.

await sock.sendMessage(jid, {
  text: '# Dashboard\n\n| Metric | Value |\n|--------|-------|\n| Users | 1.234 |\n\n```js\nconsole.log("ok")\n```\n\n> Bot running 24/7\n\n:::suggest\nCheck Status | Restart | Help\n:::',
  rich: true
})
MarkdownResult
# HeadingLarge heading
**bold**Bold
*italic*Italic
~~strikethrough~~Strikethrough
`code`Inline code
```js ... ```Code block with syntax highlighting (js, ts, python, go, java, rust)
| col1 | col2 |Table
![alt](url)Image
> quoteBlockquote
:::suggest ... :::Suggested prompt pills (recommendation buttons)

Rich response uses botForwardedMessage — Meta AI's internal message type. Only works on WhatsApp versions with AI bot support. If WhatsApp shows "Version not supported", your client doesn't have this feature yet.

Message Retry & Error Handling

@mataram/wa has an advanced automatic retry system far beyond original Baileys.

FeatureOriginal Baileys@mataram/wa
Retry cacheNoneLRU cache 512 entries, 5min TTL
Retry counterBasic counterPer-message, max configurable
Auto session recreateManualAutomatic (configurable)
Base key collisionNot checkedDetect + recreate session
MAC errorNot handledAuto recreate session
Error 463 (restricted)Not handledAuto issue tctoken + recovery
Error 479 (smax-invalid)Not handledLogged, skip retry
Retry >24hStill retriesSkip (expired)
Phone requestNoneDelayed 3s, timeout 8s

Retry Config

OptionDefaultDescription
maxMsgRetryCount5Max retry attempts per message
enableRecentMessageCachetrueCache recent messages for resend on retry
enableAutoSessionRecreationtrueAuto recreate Signal session if corrupt
getMessage() => undefinedCallback to fetch message from your DB

Signal Retry Error Codes

CodeNameCause
0UnknownErrorUnknown error
1NoSessionNo Signal session exists
2InvalidKeyInvalid key
3InvalidKeyIdWrong key ID
4InvalidMessageCorrupt message
5InvalidSignatureInvalid signature
6FutureMessageMessage from the future (counter out of sync)
7BadMacMAC check failed — auto recreate session
8InvalidSessionInvalid session — auto recreate
9InvalidMsgKeyWrong message key
10BadBroadcastEphemeralSettingWrong ephemeral broadcast setting
11UnknownCompanionNoPrekeyCompanion has no pre-key
12AdvFailureAdvanced failure
13StatusRevokeDelayStatus revoke delay
← Back Next: Error Codes →