Skip to main content
The Contacts struct provides methods for checking WhatsApp registration status and retrieving profile pictures and user information.

Access

Access contact operations through the client:

Methods

is_on_whatsapp

Check if JIDs are registered on WhatsApp. Accepts both PN JIDs and LID JIDs.
Parameters:
  • jids - Array of JIDs to check. Supports Jid::pn("phone_number") for phone number lookups and Jid::lid("lid_value") for LID lookups. If you pass a non-PN/non-LID JID (groups, newsletters, etc.), the method returns an error immediately.
Returns:
  • Vec<IsOnWhatsAppResult> - Registration status for each JID
IsOnWhatsAppResult fields:
  • jid: Jid - WhatsApp JID for the user
  • is_registered: bool - Whether the JID is on WhatsApp
  • lid: Option<Jid> - LID (Linked Identity) if available
  • pn_jid: Option<Jid> - Phone number JID, present when the server returns LID as the primary JID
  • is_business: bool - Whether this is a WhatsApp Business account
  • verified_name: Option<VerifiedName> - Decoded verified business name certificate, when the account is a verified business
  • username: Option<CompactString> - Meta username, without the display-only @ prefix. None means the server reported no username, which is also how it reports one that was deleted; it is never an empty string
  • contact_error: Option<UsyncSubprotocolError> - Server error for the contact subprotocol (e.g. privacy-blocked lookup); is_registered will be false
  • lid_error: Option<UsyncSubprotocolError> - Server error for the lid subprotocol; lid will be None
  • business_error: Option<UsyncSubprotocolError> - Server error for the business subprotocol; is_business will be false
  • username_error: Option<UsyncSubprotocolError> - Server error for the username subprotocol. Unlike the other *_error fields, this does not imply username is None. The server also publishes a username as a plain attribute of the contact result, and a failed subprotocol does not retract that value. So username can still be Some next to username_error.
VerifiedName fields:
  • name: Option<String> - Display name shown to other users (decoded from the certificate when the server omits the attribute)
  • serial: Option<String> - Certificate serial number
  • issuer: Option<String> - Certificate issuer
  • certificate: Option<Vec<u8>> - Raw VerifiedNameCertificate protobuf bytes, for callers that need to verify the signature themselves
IsOnWhatsAppResult is marked #[non_exhaustive], so new fields may be added in future versions without a breaking change.
Both is_on_whatsapp and get_user_info now ask for the username subprotocol by default, matching WhatsApp Web’s own existence check and background contact sync. This is not opt-in and adds one more child to the <query> node of both requests.
Example — phone number lookup:
Example — LID lookup:
PN and LID queries use different wire protocols (matching WhatsApp Web’s ExistsJob), so mixed inputs are automatically split into separate requests. LID-PN mappings discovered from results are persisted to the local cache.

get_profile_picture

Get the profile picture URL for a JID.
Parameters:
  • jid - Target JID (user, group, or newsletter)
  • preview - true for preview thumbnail, false for full-size image
Returns:
  • Option<ProfilePicture> - Picture info or None if not available
ProfilePicture fields:
  • id: String - Picture ID
  • url: String - Download URL
  • direct_path: Option<String> - Direct path for media download
  • hash: Option<String> - SHA-256 hash for integrity and cache validation
Example:
For groups:
To override the default request timeout for a single fetch, use get_profile_picture_with_timeout(jid, preview, timeout), which takes an extra timeout: Option<Duration> argument. Internally the request is built via ProfilePictureSpec’s with_timeout(...) builder method; pass None to fall back to the default timeout.
The system/announcements JID (0@s.whatsapp.net, and its legacy form 0@c.us) never answers this IQ, so it’s short-circuited client-side: both get_profile_picture and get_profile_picture_with_timeout return Ok(None) immediately for it instead of waiting out the full request timeout. This mirrors WhatsApp Web, which never sends the request for this JID in the first place.

get_user_info

Get user information by JID.
Parameters:
  • jids - Array of JIDs to query
The system/announcements JID (0@s.whatsapp.net, and its legacy form 0@c.us) is filtered out of the batch before it’s sent — it’s not usync-eligible and the server never answers for it. If it’s the only JID passed in, get_user_info returns an empty map without making a request; if it’s mixed with other JIDs, the remaining JIDs are still queried normally.
Returns:
  • HashMap<Jid, UserInfo> - Map of JID to user info
UserInfo fields:
  • jid: Jid - WhatsApp JID
  • lid: Option<Jid> - LID if available
  • lid_error: Option<UsyncSubprotocolError> - Server error for the lid subprotocol; lid will be None
  • status: Option<String> - Status message
  • status_error: Option<UsyncSubprotocolError> - Server error for the status subprotocol (e.g. privacy-hidden status); status will be None
  • picture_id: Option<String> - Profile picture ID
  • picture_error: Option<UsyncSubprotocolError> - Server error for the picture subprotocol; picture_id will be None
  • is_business: bool - Whether business account
  • business_error: Option<UsyncSubprotocolError> - Server error for the business subprotocol; is_business will be false
  • verified_name: Option<VerifiedName> - Decoded verified business name certificate, for verified business accounts (see is_on_whatsapp for field details)
  • devices: Vec<u16> - Device IDs from the <devices version="2"> sublist the same usync query returns (device 0 is the primary). Empty when the server omits the sublist — no extra request is needed.
  • devices_error: Option<UsyncSubprotocolError> - Server error for the devices subprotocol; devices will be empty
  • username: Option<CompactString> - Meta username, without the display-only @ prefix. See IsOnWhatsAppResult::username for how it’s sourced and what None means
  • username_error: Option<UsyncSubprotocolError> - Server error for the username subprotocol. See IsOnWhatsAppResult::username_error — a value already read from the contact result’s attribute survives next to this error
UserInfo is #[non_exhaustive], so new fields may be added in future versions without a breaking change.
UsyncSubprotocolError fields:
  • code: Option<u16> - Numeric error code from the server (e.g. 403, 404)
  • text: Option<String> - Human-readable error description
  • backoff: Option<u32> - Server-suggested retry delay in seconds
The library preserves per-subprotocol errors on the result struct rather than failing the whole request. A privacy error from one user’s status does not block retrieval of devices for other users in the same batch. Check the *_error fields when a corresponding Option field is None and you need to distinguish “not set” from “server error”.
Example:

find_by_username

Resolve a Meta username to the account behind it.
Parameters:
  • username - The handle to resolve. A leading @ is display-only and is stripped automatically. The bare handle must be between USERNAME_MIN_LENGTH (3) and USERNAME_MAX_LENGTH (35) characters, or the call fails with ContactError::Username before any request is sent.
  • username_key - The account’s numeric “username key” (WhatsApp Web calls it a pin). Some accounts require it before the server will disclose the identity behind their handle at all.
Returns: UsernameLookup:
  • NotFound - No account answers to this username, or it’s not reachable from here
  • KeyRequired { username: Option<CompactString> } - The username exists but the server withheld the identity; repeat the call with the account’s username key
  • Found(UsernameLookupUser) - The username resolved to an account
UsernameLookupUser fields:
  • jid: Jid - Identity the server returned. The query addresses contacts by LID, so this is normally a LID
  • pn_jid: Option<Jid> - Phone-number JID, when the server disclosed one on <business>
  • username: Option<CompactString> - Username as the server spelled it back
  • is_business: bool
  • verified_name: Option<VerifiedName>
On Found, the discovered LID/PN pair is persisted to the local cache, the same way is_on_whatsapp and get_user_info do.
Experimental. The request is built exactly as WhatsApp Web’s WAWebQueryExistsJob.queryUsernameExists builds it. No capture of a live server answering it backs this implementation — only the request shape is verified against the official client. A server that rejects the query is not necessarily a bug here.
Example:

Privacy & TC tokens

For user JIDs (not groups/newsletters), the library automatically includes TC tokens when fetching profile pictures. TC tokens are used for privacy-gated operations. The implementation automatically:
  • Looks up TC tokens for user JIDs
  • Includes tokens in profile picture requests
  • Skips tokens for groups and newsletters
get_user_info does the same for status/about: when the profile_scraping_privacy_token_in_about_usync AB prop is on, each queried JID’s TC token is attached to its <user> node in the usync IQ, matching WhatsApp Web’s USyncStatusProtocol. This is what lets status/status_error and about resolve correctly for a privacy-restricted contact instead of coming back hidden. See TC Token for details.

Async compatibility

is_on_whatsapp and get_user_info work correctly when called from #[async_trait] implementations or any context that boxes the returned future (Box<dyn Future + Send>). Earlier versions produced a compile error ("implementation of FnOnce is not general enough") that could not be worked around in user code. Fixed in #826 with no API changes.

Error handling

All methods return Result<T, ContactError>:
Username is returned by find_by_username when the handle can’t be turned into a valid lookup — too short, too long, or a username_key that fails usync’s own field-consistency validation.

Batch operations

All lookup methods support batch operations for efficiency:

Contact notification events

The server sends contacts notifications when contact data changes. These are emitted as events you can subscribe to:
  • ContactUpdated — a contact’s profile changed (invalidate cached presence/profile picture)
  • ContactNumberChanged — a contact changed their phone number (includes old/new JID and optional LID mappings)
  • ContactSyncRequested — the server requests a full contact re-sync
See the events reference for full struct definitions and wire format details.
These are server-push notifications, distinct from ContactUpdate and ContactRemoved, which come from app-state sync mutations made on a linked device. See Chat actions — Save and remove contacts for save_contact/remove_contact and their events.

Empty input handling

Passing empty arrays returns empty results without making network requests:

Migration from previous versions

The is_on_whatsapp method previously accepted &[&str] (phone number strings). It now takes &[Jid]:
The get_info method and ContactInfo type have been removed. Use is_on_whatsapp for registration checks (now includes lid, pn_jid, and is_business fields) or get_user_info for detailed profile data (status, picture ID).