How Baileys Supports Multi-Device Synchronization: The USync Subsystem Explained

Baileys enables multi-device synchronization through a dedicated USync subsystem that synchronizes user-wide state across all linked devices via typed protocol classes and an in-memory device cache.

The Baileys library implements WhatsApp's multi-device protocol by abstracting the underlying WebSocket binary nodes into a clean, query-based API. This article breaks down exactly how the USync subsystem works, where the critical code lives, and how you can interact with it directly.

What Is USync in Baileys?

USync is Baileys' internal mechanism for synchronizing cross-device state with WhatsApp's servers. Rather than managing raw XML-like nodes throughout the codebase, Baileys encapsulates the complexity in three core abstractions:

  • USyncQuery — Builds and parses USync requests
  • USyncUser — Identifies the target user for a query
  • Protocol classes — Handle serialization for specific data types

This design allows message sending, contact resolution, and device list fetching to share a single execution pipeline while remaining type-safe.

Core Components of the USync Subsystem

USyncQuery: The Request Builder

Located in src/WAUSync/USyncQuery.ts, the USyncQuery class constructs the WA USync request structure. It composes one or more protocol elements into a single network call.

Key methods for attaching protocols:

Method Purpose
.withDeviceProtocol() Fetch linked devices for a user
.withContactProtocol() Retrieve contact information
.withStatusProtocol() Get user status updates
.withLIDProtocol() Resolve LID to JID mapping

Each method returns this for fluent chaining.

USyncUser: Target Specification

In src/WAUSync/USyncUser.ts, the USyncUser class builds the <user> element that identifies who the query targets. It supports three identification modes:

// By phone number
new USyncUser().withPhone('1234567890')

// By JID (WhatsApp ID)
new USyncUser().withId('1234567890@s.whatsapp.net')

// By LID (push notification ID)
new USyncUser().withId('lid:some-long-identifier')

Protocol Classes: Type-Specific Serialization

The src/WAUSync/Protocols/ directory contains implementations like:

  • USyncDeviceProtocol — Serializes device list requests
  • USyncContactProtocol — Handles contact data
  • USyncStatusProtocol — Manages status retrieval
  • USyncLIDProtocol — Maps LID to JID

Each protocol implements getUserElement() to produce its XML node and knows how to parse the corresponding response node.

Executing USync Queries

The executeUSyncQuery method in src/Socket/socket.ts (around line 270) is the central dispatch point:

const result = await socket.executeUSyncQuery(query)

This method:

  1. Sends the built query via the internal query helper
  2. Waits for the server response with a timeout
  3. Returns a typed USyncQueryResult containing parsed data

The result structure includes result.list, where each entry contains the deserialized protocol responses.

Device Cache Integration

Baileys maintains an in-memory userDevicesCache on each socket to avoid redundant network calls. The cache is refreshed automatically when:

  • The cached entry is stale
  • An incoming "device-notification" stanza arrives

The getUSyncDevices helper in src/Socket/messages-send.ts (line 241) wraps this logic, ensuring message sending always uses current device information.

Practical Code Examples

Initialize a Multi-Device Socket

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

const { state, saveState } = await useMultiFileAuthState('auth_info')
const sock = makeWASocket({
  auth: state,
  printQRInTerminal: true,
  // multi-device is the default; no explicit flag needed
})

Refresh Device List for a User

import { USyncQuery, USyncUser } from '@adiwajshing/baileys'

async function refreshDevices(jid: string): Promise<void> {
  const query = new USyncQuery()
    .withDeviceProtocol()                       // request device list
    .withUser(new USyncUser().withId(jid))      // target this JID

  const result = await sock.executeUSyncQuery(query)
  const devices = result?.list?.[0]?.devices ?? []
  
  console.log(`Found ${devices.length} devices for ${jid}:`, devices)
}

Resolve LID to JID for Push Notifications

async function jidFromLID(lid: string): Promise<string | undefined> {
  const query = new USyncQuery()
    .withLIDProtocol()
    .withUser(new USyncUser().withId(lid))

  const result = await sock.executeUSyncQuery(query)
  return result?.list?.[0]?.jid
}

This mapping is critical for routing push notifications to the correct device, as implemented in src/Signal/lid-mapping.ts.

Multi-Device Synchronization Workflow

  1. Socket creation — makeWASocket initializes with multi-device enabled by default
  2. Query construction — Code creates new USyncQuery() and attaches required protocols
  3. User attachment — A USyncUser specifies the target via phone, JID, or LID
  4. Network execution — executeUSyncQuery sends the request and parses the response
  5. Cache population — Results update userDevicesCache for subsequent operations

All message sending in messages-send.ts follows this flow to ensure every linked device receives messages.

Key Source Files Reference

File Purpose
src/WAUSync/USyncQuery.ts Core USync request builder and parser
src/WAUSync/USyncUser.ts User element construction
src/WAUSync/Protocols/* Protocol-specific serialization
src/Socket/socket.ts executeUSyncQuery implementation
src/Socket/messages-send.ts Device cache usage in message sending
src/Signal/lid-mapping.ts LID-to-JID resolution helper

Summary

  • USync is Baileys' abstraction over WhatsApp's multi-device synchronization protocol
  • USyncQuery chains protocols fluently to build complex requests
  • executeUSyncQuery in socket.ts provides the single entry point for execution
  • userDevicesCache minimizes network overhead by caching device lists
  • LID resolution enables correct routing of push notifications across linked devices

The subsystem's clean separation between query construction, protocol serialization, and network execution makes Baileys' multi-device support maintainable and extensible.

Frequently Asked Questions

Is multi-device mode mandatory in Baileys?

Yes. As of recent versions, Baileys operates exclusively in multi-device mode. The legacy single-device protocol is no longer supported, so makeWASocket always initializes with multi-device capabilities enabled.

How does Baileys handle device list staleness?

Baileys uses an in-memory userDevicesCache that stores device lists with metadata. The cache is invalidated and refreshed when explicit device-notifications arrive from the server or when a cached entry expires based on internal TTL logic.

Can I query multiple users in a single USync request?

The USyncQuery API accepts a single USyncUser, but WhatsApp's protocol technically supports multiple users per request. However, Baileys' current implementation primarily uses one-to-one queries for simplicity and clearer error handling.

What is a LID and why does it matter for multi-device?

LID (Linked Device ID) is WhatsApp's internal push-notification identifier. Unlike JIDs, LIDs are stable across device changes but aren't human-readable. Baileys queries USyncLIDProtocol to map LIDs to JIDs so messages and notifications route correctly when a user adds or removes devices.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →