Skip to main content

ADR 002: WhatsApp Integration โ€” Twilio Meta Cloud API + Number Pool

Date: 2026-04-30 Status: Accepted (Twilio + pool model). The sender-whitelist routing decision below was superseded on 2026-05-05 โ€” see Update section.


Decisionโ€‹

Use Twilio as the Business Solution Provider (BSP) for Meta's WhatsApp Cloud API. Pre-provision a standing pool of ~20 WhatsApp numbers (one per Specialist persona) rather than provisioning numbers per client Org.

Architectureโ€‹

  • One number per Specialist from a pre-provisioned pool
  • Twilio subaccounts: staging and production each have separate credentials
  • Sender whitelist per Org controls who can initiate conversations (superseded โ€” see Update below)
  • Inbound routing: message arrives on Specialist number โ†’ lookup sender โ†’ find Org โ†’ route to Expert Queue

Rationaleโ€‹

  • Official Meta Cloud API โ€” no ToS violation, no ban risk
  • Numbers scale with Specialists (~20), not Orgs (could be hundreds)
  • Free trial Orgs cost nothing extra
  • Twilio subaccount isolation keeps prod credentials away from developers

Consequencesโ€‹

  • Requires one-time Meta Business Account verification
  • Each Specialist number costs ~$1/month
  • Outbound messages cost ~$0.005 (Twilio) + Meta per-message rate

Update โ€” 2026-05-05: Pairing codes supersede the sender whitelistโ€‹

What changed: The "Sender whitelist per Org controls who can initiate conversations" mechanism is replaced by a self-service pairing-code flow. The original whitelist is retained only as a legacy routing fallback for orgs onboarded before this change; new orgs do not configure it.

New mechanism:

  • Each user generates a one-time HW-XXXX code from the portal (Settings โ†’ WhatsApp), 15-minute TTL, stored in whatsapp_pairing_codes.
  • Texting that code from WhatsApp links the sender's phone to (userId, orgId) via a new whatsapp_linked_numbers table.
  • The inbound handler routes by checking, in order: (1) pairing-code message, (2) whatsapp_linked_numbers, (3) whatsapp_sender_whitelist (fallback), (4) unknown-sender auto-reply.
  • Re-pairing the same phone to a different org is rejected without burning the code; the existing link must be removed from the dashboard first.

Why the change:

  • The whitelist required AMs to know each client's phone number in advance โ€” fragile and high-friction.
  • Number changes silently broke routing.
  • No self-service path for clients; every change required HP intervention.
  • Pairing captures the phone at link time (self-reported by the device), supports re-linking on number changes, and is fully self-service.

Tracking: GitHub issue #191 (closed on landing).

Related docs: docs/features/channels.md (WhatsApp section), docs/ops/TWILIO-SETUP-CHECKLIST.md (Sections 8 & 9).