Modern WhatsApp identifies users in two different ways simultaneously: by their phone number and by an opaque long-form device identifier called a LID. WABotJS tracks both forms and provides a set of resolution utilities so your bot can work with either format transparently. Understanding how JID resolution works helps you write correct message routing logic and diagnose cases where a contact’s identity cannot yet be determined.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/jzszdznzzl/WABotJS/llms.txt
Use this file to discover all available pages before exploring further.
Background
WhatsApp JIDs come in several shapes, but for individual users only two matter at the application level:PN JID — phone-number based
Format:
{country_code}{number}@s.whatsapp.netExample: 15551234567@s.whatsapp.netThe traditional JID format, derived directly from the registered phone number.LID JID — opaque identifier
Format:
{opaque_id}@lidExample: 1234567890123456789@lidA long-form identifier introduced by WhatsApp’s multi-device and privacy architecture. It is stable across number changes and device migrations.remoteJid / remoteJidAlt for chat-level JIDs and participant / participantAlt for sender-level JIDs inside groups. WABotJS normalises toward LID where possible because it is the preferred modern identifier.
Group JIDs (@g.us) and broadcast JIDs are a separate category and are not resolved by the JID store — they are used directly as-is.
The JID Store
Stores.JID (accessed as bot.cache.jid) is a persistent, bidirectional mapping table stored in jid_store.sqlite inside your datadir. It is backed by Utils.SQLiteStore with no TTL, so mappings accumulate permanently across bot restarts.
How it is populated
The store attaches itself to the socket viabind(sock) during bot.login() and listens to two events:
messages.upsert
messages.upsert
For every incoming message whose
type is 'notify', the store checks:- If
m.key.remoteJidandm.key.remoteJidAltare both set, it callsUtils.resolveLIDAndPN()on the pair. If both a LID and a PN are found, the bidirectional mapping is written to SQLite. - If
m.key.participantandm.key.participantAltare both set (group messages), the same resolution and storage logic runs for the participant pair.
connection.update (open)
connection.update (open)
Immediately after the connection opens, the store resolves the bot’s own identity from
sock.user.id, sock.user.lid, and sock.user.phoneNumber. If both a LID and a PN can be found, the bot’s own mapping is stored — ensuring the bot’s JID is always resolvable from the very first event.Internal storage layout
Each mapping is stored as two rows — one keyed by the LID and one keyed by the PN — so lookups in either direction are O(1) SQL point reads:Uint8Array) in a SQLite BLOB column, normalised through baileys.jidNormalizedUser() before writing.
Resolving a JID
Usebot.cache.jid.resolve(jid) to look up the counterpart of any known JID:
resolve(jid: string): { lid: string; pn: string } | undefined
resolve() accepts either a LID or a PN JID. If the input is not a recognised LID or PN format (determined by Baileys’ isLidUser() and isPnUser() guards), it returns undefined immediately without querying SQLite.
Resolution depends on having previously seen both JID forms in the same message. Brand-new contacts or fresh accounts will return
undefined until at least one message is exchanged that carries both remoteJid and remoteJidAlt.Utility functions
WABotJS exports three standalone resolution helpers underUtils for use in your own code:
Utils.resolveLID(...args)
Finds the first LID JID among its arguments (filtering out non-strings), normalises it with baileys.jidNormalizedUser(), and returns it. Returns undefined if no LID JID is found.
Utils.resolvePN(...args)
Finds the first PN JID among its arguments, normalises it, and returns it. Returns undefined if no PN JID is found.
Utils.resolveLIDAndPN(...args)
Calls both resolveLID and resolvePN on the same argument list. Returns { lid, pn } only if both are found; otherwise returns undefined.
Stores.JID and Stores.Message, but you can use them freely for any routing or normalisation logic in your bot.
How Message uses resolution
TheMessage class (the object passed to your onMessage and onCommand handlers) uses Utils.resolveLID() and Utils.resolvePN() internally when setting its chat and sender fields. Where both a LID and a PN appear on the raw Baileys message key, Message prefers the LID for chat and sender so your handler always receives the modern identifier format. This means you can pass m.sender or m.chat directly to bot.cache.jid.resolve() and expect a consistent result.
Group JIDs
Group JIDs use the@g.us suffix (e.g. 120363000000000001@g.us). They are not handled by Stores.JID — resolve() returns undefined immediately for any JID that is not a LID or PN user. To work with group identity use bot.sock.groupMetadata(groupJid) or read from bot.cache.metadata directly.
Resolution depends on seeing both JID forms together in the same message event. On a fresh account or with a contact you have never exchanged messages with,
resolve() will return undefined until at least one message is received that carries both remoteJid / remoteJidAlt (or participant / participantAlt) simultaneously.