Dentolize · Hidden-Number WhatsApp Contacts Walkthrough
On this pageWhy this mattersRisk profile of the changeWhat "done" looks like for this PR

For Stakeholders

Why this matters

Before this PR, a WhatsApp contact using a standard, opt-in WhatsApp privacy setting could send a clinic a message that Dentolize's backend would fail to store correctly (Argument 'phone' is missing), and even where a conversation somehow existed, a reply to that contact would silently fail to deliver. Both failure modes are invisible to the clinic — nothing in the UI would have surfaced "this message failed" — so the actual impact prior to this fix is not fully knowable from the code alone; it depends on how many real contacts hit this path in production.

Risk profile of the change

  • Data model risk: the migration widens OnlineConversation.phone from required to optional and adds a second unique index. It's additive and non-destructive — no existing data is rewritten, no existing row can violate the new constraint (a nullable bsuid doesn't clash with itself; Postgres unique indexes ignore NULLs). Low risk.
  • Behavioral risk in the conflict case: if a contact's phone and BSUID already resolve to two different existing conversation rows (plausible for conversations that predate this migration), the code deliberately does not merge them or update either — it silently prefers the phone-matched row and leaves the BSUID row untouched. This avoids crashing or corrupting data, but it means a data-quality issue (two rows for one contact) is preserved rather than fixed. See Identifying a Contact.
  • Incomplete coverage: hidden-number contacts don't get auto-created as leads, don't support the duplicate-contact check, and can't receive online-payment links. None of these are silent failures anymore (each degrades explicitly/gracefully), but none were built out either — this PR fixes the crash and delivery paths, not full feature parity for hidden-number contacts.
  • Bundled, unrelated fix: whatsapp-official's environment-variable load order was also fixed in this branch (a boot-time crash unrelated to the BSUID work). Bundling it here means it ships with this change rather than independently, which is a minor scope-creep note for release tracking, not a risk.

What "done" looks like for this PR

Every code path that used to assume a phone number was present has been updated to handle its absence — inbound webhook parsing, outbound reply addressing (both the standalone whatsapp-official service and the main GraphQL server), patient/lead matching, and every UI surface that displays a conversation's address. The one deliberately deferred piece is merging conflicting historical rows, called out explicitly in the code and in Identifying a Contact as a separate future job.