The Contacts struct provides methods for checking WhatsApp registration status and retrieving profile pictures and user information.
Access
Access contact operations through the client:
let contacts = client.contacts();
Methods
is_on_whatsapp
Check if JIDs are registered on WhatsApp. Accepts both PN JIDs and LID JIDs.
pub async fn is_on_whatsapp(
&self,
jids: &[Jid],
) -> Result<Vec<IsOnWhatsAppResult>, anyhow::Error>
Parameters:
jids - Array of JIDs to check. Supports Jid::pn("phone_number") for phone number lookups and Jid::lid("lid_value") for LID lookups. Non-PN/non-LID JIDs (groups, newsletters) are skipped with a warning.
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
IsOnWhatsAppResult is marked #[non_exhaustive], so new fields may be added in future versions without a breaking change.
Example — phone number lookup:
let results = client.contacts().is_on_whatsapp(&[
Jid::pn("15551234567"),
Jid::pn("15559876543"),
]).await?;
for result in results {
if result.is_registered {
println!("{} is on WhatsApp", result.jid);
if let Some(lid) = &result.lid {
println!(" LID: {}", lid);
}
if result.is_business {
println!(" Business account");
}
} else {
println!("{} is NOT on WhatsApp", result.jid);
}
}
Example — LID lookup:
let results = client.contacts().is_on_whatsapp(&[
Jid::lid("100000001"),
]).await?;
for result in results {
if let Some(pn) = &result.pn_jid {
println!("LID {} maps to phone {}", result.jid, pn);
}
}
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.
pub async fn get_profile_picture(
&self,
jid: &Jid,
preview: bool,
) -> Result<Option<ProfilePicture>, anyhow::Error>
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:
let jid: Jid = "15551234567@s.whatsapp.net".parse()?;
// Get preview thumbnail
if let Some(preview) = client.contacts().get_profile_picture(&jid, true).await? {
println!("Preview URL: {}", preview.url);
println!("Picture ID: {}", preview.id);
}
// Get full-size picture
if let Some(full) = client.contacts().get_profile_picture(&jid, false).await? {
println!("Full URL: {}", full.url);
if let Some(path) = full.direct_path {
println!("Direct path: {}", path);
}
}
// Handle user with no profile picture
let no_pic_jid: Jid = "15559999999@s.whatsapp.net".parse()?;
if client.contacts().get_profile_picture(&no_pic_jid, true).await?.is_none() {
println!("User has no profile picture");
}
For groups:
let group_jid: Jid = "123456789@g.us".parse()?;
if let Some(pic) = client.contacts().get_profile_picture(&group_jid, true).await? {
println!("Group picture: {}", pic.url);
}
get_user_info
Get user information by JID.
pub async fn get_user_info(
&self,
jids: &[Jid],
) -> Result<HashMap<Jid, UserInfo>, anyhow::Error>
Parameters:
jids - Array of JIDs to query
Returns:
HashMap<Jid, UserInfo> - Map of JID to user info
UserInfo fields:
jid: Jid - WhatsApp JID
lid: Option<Jid> - LID if available
status: Option<String> - Status message
picture_id: Option<String> - Profile picture ID
is_business: bool - Whether business account
Example:
let jids = vec![
"15551234567@s.whatsapp.net".parse()?,
"15559876543@s.whatsapp.net".parse()?,
];
let user_info = client.contacts().get_user_info(&jids).await?;
for (jid, info) in user_info {
println!("User: {}", jid);
println!(" Business: {}", info.is_business);
if let Some(status) = info.status {
println!(" Status: {}", status);
}
if let Some(pic_id) = info.picture_id {
println!(" Picture ID: {}", pic_id);
}
}
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
Error handling
All methods return Result<T, anyhow::Error>. Common errors:
- Invalid JID format
- Network errors
- Rate limiting
match client.contacts().is_on_whatsapp(&[Jid::pn("15551234567")]).await {
Ok(results) => {
for result in results {
println!("Registered: {}", result.is_registered);
}
}
Err(e) => eprintln!("Failed to check registration: {}", e),
}
Batch operations
All lookup methods support batch operations for efficiency:
// Check multiple JIDs at once
let results = client.contacts().is_on_whatsapp(&[
Jid::pn("15551111111"),
Jid::pn("15552222222"),
Jid::pn("15553333333"),
]).await?;
// Get info for multiple JIDs
let jids = vec![
"15551111111@s.whatsapp.net".parse()?,
"15552222222@s.whatsapp.net".parse()?,
];
let user_info = client.contacts().get_user_info(&jids).await?;
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
Event::ContactUpdated(update) => {
// Refresh cached profile data for update.jid
}
Event::ContactNumberChanged(change) => {
// Migrate chat data from change.old_jid to change.new_jid
}
Event::ContactSyncRequested(sync) => {
// Re-sync contacts (optionally filtered by sync.after timestamp)
}
See the events reference for full struct definitions and wire format details.
Passing empty arrays returns empty results without making network requests:
let empty: Vec<Jid> = vec![];
let results = client.contacts().is_on_whatsapp(&empty).await?;
assert!(results.is_empty());
Migration from previous versions
The is_on_whatsapp method previously accepted &[&str] (phone number strings). It now takes &[Jid]:
// Before
let results = client.contacts().is_on_whatsapp(&["1234567890"]).await?;
// After
let results = client.contacts().is_on_whatsapp(&[Jid::pn("1234567890")]).await?;
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).