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
| Axis | Choice | Why |
|---|---|---|
| Transport | Direct 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 model | Per-org numbers | Only path that delivers the CTWA referral hook — it appears solely on the lead’s first message to the coach’s own number. |
| v1 scope | Inbound capture only | Webhook → 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_connections—provider = whatsapp_cloud,external_account_id = phone_number_id.lead_attribution—external_lead_id = ctwa_clid,source_url/headline/body= the CTWA creative.lead_campaigns—channel = whatsapp,external_ref = ctwa source_id.- Shared
lead-ingestionqueue, EventTracking, CredentialEncryption, and the same app-secret webhook HMAC (verifyMetaSignaturereused 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_campaignskeyed onreferral.source_id, named fromheadline lead_attribution:external_lead_id = ctwa_clid,source_url/headline/bodyfrom 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
| Phase | What | Status |
|---|---|---|
| WA-1 | /webhooks/whatsapp (verify + HMAC), route by phone_number_id → connection, enqueue CTWA messages on the shared queue (job whatsapp) | ✅ a7729487 |
| WA-2 | WhatsAppProcessorService: referral → hook → lead → attribution; idempotent | ✅ a7729487 |
| WA-3 | Per-org Embedded Signup connect flow → integration_connections + token; “WhatsApp” card on the Integrations tab | ✅ backend c3a9dfbf + frontend card/popup |
| WA-4 | Unit 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), thenWHATSAPP_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.