Check Whether a WhatsApp Contact Is Saved via API
To check whether a person is saved in the connected WhatsApp account’s contact list, use UnifyPort’s GET /v1/accounts/{account_id}/contacts/membership with the contact’s returned LID as contact_id. A successful response with data.is_in_contacts: false means not saved, not a failed request. This checks the connected account’s list—not whether the other person saved your number.
Key takeaways
- Use the LID returned as
conversation_idby the contact APIs; do not construct it from a phone number. - Keep three application outcomes: saved, not saved, and unknown.
- Contact-list membership is separate from chat history, block status, and permission to send marketing.
- Checking is read-only. Adding a contact must remain a separate, deliberate action.
What does the saved-contact check establish?
WhatsApp’s official contact guide says deleting a contact does not delete the chat history. A chat row is therefore not evidence that its participant is still saved. The same guide describes sharing a contact and the recipient subsequently choosing to add it or message it: sharing details and saving them are different operations.
The UnifyPort membership reference answers a narrower question: does this contact belong to this connected account’s contact list?
| Question | Appropriate evidence |
|---|---|
| Is this person saved by the connected account? | Successful membership response and is_in_contacts |
| Has the other person saved our number? | Not answered by this endpoint |
| Is this person blocked? | Separate blocklist workflow |
| Does a chat exist, or did someone share a contact card? | Separate conversation or message evidence |
| May our application contact this person? | Your independently established permission and communication policy |
For address-book writes versus sharing details, read Add a contact vs send a vCard. Do not call the add endpoint merely to make a membership check easier: that changes the state you intended to inspect.
Prepare the account and the returned LID
UnifyPort provides an unofficial interface for a connected messaging account; this is not a WhatsApp Cloud API endpoint. The current provider action support reference maps membership checks to whatsapp, not LINE, Telegram, Zalo, or the separate whatsapp-protocol provider.
Keep the selected account and contact together. Contact objects can expose different id, provider_user_id, and conversation_id values. For this WhatsApp operation, the documented input is the LID returned as conversation_id by contact APIs. Retain it from a legitimate existing contact workflow; never append @lid to a phone number.
If your application has only a phone number and no verified returned LID, leave the check unavailable until identity is resolved. This endpoint is not documented as an arbitrary phone-number lookup.
The following illustrative request has not been executed here. Populate the environment variables from your backend’s selected account and stored contact mapping; keep the API key off the browser:
curl --get \
"https://api.unifyport.ai/v1/accounts/${UNIFYPORT_ACCOUNT_ID}/contacts/membership" \
-H "X-Api-Key: ${UNIFYPORT_API_KEY}" \
--data-urlencode "contact_id=${WHATSAPP_CONTACT_LID}"
Encoding the query preserves identifiers containing @. The successful response contains data.contact_id and the boolean data.is_in_contacts.
Render three outcomes, not one checkbox
A common application mistake is converting every falsy or missing value to “not saved.” That turns a timeout, unsupported provider, or malformed response into a false conclusion.
Use an explicit result classifier. This illustrative JavaScript interprets an already captured HTTP status and parsed JSON; it performs no network call:
function classifyMembership(httpStatus, body) {
if (httpStatus !== 200) return 'unknown';
const value = body?.data?.is_in_contacts;
if (value === true) return 'saved';
if (value === false) return 'not_saved';
return 'unknown';
}
These strings are application labels, not API response fields. Handle connection failures and non-JSON responses as unknown before calling the classifier.
| Observation | UI result | Recommended next action |
|---|---|---|
200 and boolean true | Saved | Show the observation time |
200 and boolean false | Not saved | Offer a separate authorized add action only if needed |
501 unsupported_by_provider | Unsupported / unknown | Check the selected provider; do not add automatically |
| Timeout, failed response, or missing boolean | Unknown | Keep diagnostics and investigate or refresh |
Store observations with the account, contact LID, and your local check time. Do not share one cached result across messaging accounts. Ignore a late response if the operator has since selected a different contact, and do not present an old observation as a permanent guarantee.
Keep follow-up actions independent
A negative result is not permission to write. If the operator genuinely wants to save the person, use the separate Add contact reference, retain its returned identifiers, and check membership again when current confirmation is needed.
Likewise, “not saved” is not “blocked.” The mute versus block guide explains the independent controls. Do not unblock, send a test message, or modify the address book as a side effect of displaying membership.
For failures, retain HTTP status and request_id when available, without logging API keys or entire contact records. Application access controls should restrict who can inspect each account’s contacts.
FAQ
Can this tell whether someone saved my number?
No. It checks membership in the connected account’s contact list, not the other person’s address book.
Is is_in_contacts: false an error?
Not in a successful 200 response. It is a successful negative result. Missing data or a failed request is unknown, not false.
Can I supply a phone-number JID instead of a LID?
The documented WhatsApp input is the LID returned as conversation_id by contact APIs. Use that value, not a guessed identifier.
Does receiving a contact card prove that it was saved?
No. A contact message and an address-book operation are separate. Check membership when that distinction matters.
Next step and sources
Build the read-only control against Check contact membership. Before release, test true, false, missing data, an unsupported provider, a timeout, and a contact-selection change while a request is pending. These are proposed checks, not reported test results.
References checked on 2026-10-31:
Turn messaging integration into a stable product pipeline.
Start by sending through one API, then bring every inbound message back into your business system with standard events.