Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesLeads CrmWhatsApp Lead Ingestion (P3)

WhatsApp Lead Ingestion (P3)

Status: In progress (PR #138) — inbound pipeline + connect flow (backend + frontend) built; gated on Meta App Review before a real number goes live. Canonical pipeline doc: lead-ingestion.md — the shared pipeline + both channels. This page keeps the WhatsApp-specific decisions + Meta gates. Parent spike: spike-social-whatsapp-lead-ingestion.md Builds on: the Meta Lead Ads pipeline (P2) in apps/api/src/lead-ingestion/.

Locked decisions

AxisChoiceWhy
TransportDirect Meta Cloud API (no BSP)$0 fees (Meta-hosted; inbound conversations free); reuses our Meta app/Graph/OAuth; full CTWA attribution. Cost is Tech Provider review time, not money. (360dialog’s ~$600 subscription rejected.)
Number modelPer-org numbersOnly path that delivers the CTWA referral hook — it appears solely on the lead’s first message to the coach’s own number.
v1 scopeInbound capture onlyWebhook → lead + hook. Outbound nurture is the automations whatsapp.channel (FIT-140), later.

Goal

A lead taps a Click-to-WhatsApp ad → messages the coach’s number → we auto-create a lead stamped with the ad as its hook, grouped like any other lead source.

Why this is cheap: the P2 model already absorbs WhatsApp → 0 migrations

  • integration_connectionsprovider = whatsapp_cloud, external_account_id = phone_number_id.
  • lead_attributionexternal_lead_id = ctwa_clid, source_url/headline/body = the CTWA creative.
  • lead_campaignschannel = whatsapp, external_ref = ctwa source_id.
  • Shared lead-ingestion queue, EventTracking, CredentialEncryption, and the same app-secret webhook HMAC (verifyMetaSignature reused verbatim).

The CTWA mechanism

The first inbound message from a CTWA ad carries a referral: source_id (ad), headline, body (ad copy), ctwa_clid (click id). The webhook (object: whatsapp_business_account) delivers messages[] + contacts[] (profile name, wa_id = phone). Mapping:

  • lead.phone = wa_id, lead.name = profile name, source = whatsapp
  • hook bucket = lead_campaigns keyed on referral.source_id, named from headline
  • lead_attribution: external_lead_id = ctwa_clid, source_url/headline/body from the referral, raw = payload
  • idempotency on the click/message id; dedup on phone (reuses OrganizationLeadsService.createLead)

v1 rule — what creates a lead

Only messages carrying a CTWA referral (i.e. from a Click-to-WhatsApp ad). Organic inbound is intentionally ignored so ordinary member chats don’t become leads. Fast-follow option: broaden to “any new non-member inbound”.

Build phases

PhaseWhatStatus
WA-1/webhooks/whatsapp (verify + HMAC), route by phone_number_id → connection, enqueue CTWA messages on the shared queue (job whatsapp)a7729487
WA-2WhatsAppProcessorService: referral → hook → lead → attribution; idempotenta7729487
WA-3Per-org Embedded Signup connect flow → integration_connections + token; “WhatsApp” card on the Integrations tab✅ backend c3a9dfbf + frontend card/popup
WA-4Unit tests (mirror the Meta specs) + i18n
(later)Outbound (automations whatsapp.channel / FIT-140), concierge, Conversions API⬜ out of scope

Ingestion is gated by an active connection (no feature flag), same as Meta. WA-1/WA-2 are testable now against Meta’s free WhatsApp test number.

Env

WHATSAPP_VERIFY_TOKEN (falls back to META_VERIFY_TOKEN), WHATSAPP_EMBEDDED_SIGNUP_CONFIG_ID. Reuses META_APP_ID / META_APP_SECRET and PAYMENT_CREDENTIALS_ENCRYPTION_KEY.

Meta gates (all $0, on the existing Taikan app)

  • Add the WhatsApp product/use case → free test number to build against.
  • Tech Provider verification + App Review for whatsapp_business_management + whatsapp_business_messaging (incremental on the Lead Ads review).
  • Business Verification shared with Lead Ads.
  • Per-org: number registration + display-name approval (no VoIP numbers).

Open items / follow-ups

  • Go-live — needs Meta Business Verification + App Review (whatsapp_business_management, whatsapp_business_messaging), then WHATSAPP_EMBEDDED_SIGNUP_CONFIG_ID + a registered number for a live end-to-end. Testable now against the free WhatsApp test number.
  • Shared LeadUpsertService — the campaign-resolve + lead-upsert helpers are duplicated across the Meta and WhatsApp processors; extract when a 3rd channel (email) lands.