Skip to content

WhatsApp Identity Model

Meta is rolling out WhatsApp Usernames and Business Scoped User IDs (BSUID) for Cloud API. Customers may message a business without exposing their phone number.

ConnectGain stores multiple WhatsApp identifiers per contact and matches inbound messages using this priority:

  1. whatsapp_bsuid
  2. whatsapp_jid (including @lid privacy JIDs on Baileys/WhatsApp Lite)
  3. whatsapp_phone / contacts.phones[]
  4. whatsapp_username (match only — outbound send requires phone, BSUID, or JID)

Schema

Dedicated columns on contacts:

Column Purpose
whatsapp_phone Normalized E.164 when known
whatsapp_jid Full JID (@s.whatsapp.net, @lid, etc.)
whatsapp_username @handle without leading @
whatsapp_bsuid Meta Business Scoped User ID

Indexed via contact_whatsapp_identity_index (service-role lookups at webhook scale).

Inbound flow

All three WhatsApp webhooks parse identifiers and call findOrCreateContactByWhatsAppIdentity:

  • whatsapp-cloud-webhook — Meta Cloud API / Pabbly
  • appgain-whatsapp-webhook — primary WhatsApp Lite (Appgain/Baileys)
  • whatsapp-lite-webhook — legacy flat Appgain payload

Raw identity snapshots are stored on messages.metadata.whatsapp_identity for monitoring.

Contacts without a resolvable phone receive the whatsapp_phone_pending tag.

Outbound flow

Send functions resolve recipients via resolveWhatsAppRecipient:

  • Phone → Meta to / Appgain mobileNum
  • BSUID → Meta to (Cloud API)
  • JID → Appgain mobileNum (Lite; logged as phone_missing_jid_send)

Phone request flow

When a contact has BSUID/JID/username but no phone:

  1. Tag: whatsapp_phone_pending
  2. Agent or bot sends a message asking the customer to share their phone number
  3. On next inbound with phone, identities merge onto the existing contact (no duplicate)

Monitoring (Baileys / Lite)

Lite webhooks emit structured logs with event whatsapp_identity_anomaly when:

  • username is present
  • @lid JID is seen
  • Phone is missing but another identifier exists

Key files

File Role
the platform Parse, match, merge, send resolution
the platform Schema + RPC
the platform Frontend send guards

Testing

npm test -- --run src/lib/__tests__/whatsapp-identity.test.ts

Test matrix:

  • Inbound with phone only (regression)
  • Inbound with username only
  • Inbound with BSUID only
  • Existing phone contact later messages via username → merge
  • Outbound by BSUID/JID when phone null
  • Phone-request CTA → phone arrives → identity merge