Skip to content

ADR-0029 · WhatsApp communication platform via Meta Cloud API, direct ​

Status: 🟡 Proposed — direction set by the owner 2026-08-21 (Meta app "QR Setu" already activated; explicit instruction to avoid third-party WhatsApp providers), recorded here with its costs stated · Depends on: ADR-0008 (channel split), ADR-0027 (outbox enqueue discipline — the drain worker is shared scope), ADR-0021 (merchant campaign gating), ADR-0028 (admin vs org trust boundaries) · Amends: ADR-0008 (WhatsApp becomes the primary customer-facing notification channel; email keeps OTP and account mail)

Context ​

QRSETU needs off-app communication for both sides of every transaction: buyers (mostly anonymous, no app, phone number known — orders.buyer_phone is NOT NULL) and merchants (signup, orders, payouts, proactive nudges). The owner's direction is WhatsApp-first for India, explicitly avoiding SMS/DLT infrastructure at this stage, and integrating Meta's official APIs directly rather than a third-party WhatsApp platform (WATI, MSG91, or any BSP).

The Meta capability surface was verified against live documentation on 2026-08-21 — the full matrix, with citations and per-claim verification tags, is the portal's Meta API assessment. The facts that decide this ADR:

  • A business messaging from its own WABA needs no App Review (Standard Access, direct developer) — Phase 1 is buildable immediately with a System User token.
  • Send, receive, template lifecycle, quality/limit signals, and usage/cost analytics (pricing_analytics) are all first-party APIs with webhooks.
  • The two things Meta does NOT offer are billing automation (no API for payment methods, invoices, or any top-up concept) and prepaid balances — which are precisely the conveniences BSPs resell.
  • Meta enforces quality economically (per-user marketing caps live in India, template pausing, tier drops), which any architecture must treat as policy, not transience.

Options considered ​

  • For: no per-message BSP margin on top of Meta's rates; one system of record; first-party webhooks and analytics; no App Review for our own WABA; full control of the template registry and consent model; the future Tech Provider path stays open.
  • Against / the costs, stated: we own webhook infrastructure, retry, template ops and quality management ourselves; billing is a manual Billing Hub runbook (true on every path — BSPs merely hide it); Meta's doc churn is ours to track.

B — BSP (WATI, MSG91, Interakt, …) ​

  • For: faster first send; prepaid wallets and invoicing UI; support.
  • Against: per-message margin forever; a second system of record for templates/consent; vendor lock-in on exactly the layer QRSETU intends to expose to its own tenants later; contradicts the owner's explicit instruction.

C — Hybrid (direct for transactional, BSP for marketing) ​

  • Against: two template registries, two consent stores, two webhook shapes — the duplicate-source-of-truth class (QRS-249) bought deliberately. Rejected.

D — SMS/DLT first ​

  • Against: DLT registration overhead, worse medium for the audience, and the owner has explicitly deferred it. WhatsApp does not preclude adding SMS later behind the same channel-agnostic envelope.

Decision (proposed) ​

  1. Direct Meta integration, one Meta app ("QR Setu"), one platform WABA, INR-billed from creation (India INR migration is mandatory by 2026-12-31).
  2. A channel-agnostic envelope with a single dispatcher choke point — features enqueue (message row + outbox comm.send, same transaction as the business write); only the dispatcher talks to Meta; only the webhook EF listens. Design: architecture · data model.
  3. QRSETU is the single sender of record in R1. Per-tenant WABAs (Embedded Signup, Tech Provider, Advanced Access) are a later programme; the schema carries workspace_id / owner_workspace_id from the first migration so that programme changes rows, not shape. ⚠ ADR-0030 now decides that programme in detail — including that a customer's own branding and QRSETU-sold prepaid credits are mutually exclusive without Solution Partner status, and that presenting a customer's name on a number inside QRSETU's own WABA is prohibited rather than a shortcut.
  4. Consent fails closed for marketing, keyed by phone (anonymous-first); transactional consent is the transaction itself, per Meta's opt-in policy and DPDP.
  5. The Admin Communication Hub is a platform-admin surface (module contract); it is a design REQUEST to Claude Design (no admin-panel screen exists), and its access is gated on the is_admin() decision (QRS-803), which this ADR does not preempt.
  6. Phasing: P1 transactional utility on the money path → P2 template/usage operations → P3 marketing campaigns → P3+ tenant senders and in-chat payments. The in-chat-payments option is explicitly blocked on verifying Razorpay Route/split compatibility with WhatsApp-originated payments (undocumented at Meta) before any design.

Consequences ​

  • Two open prerequisites are inherited, not created: the outbox has no drain worker (shared with ADR-0025/0027), and the outbox topic CHECK needs comm.send added by migration.
  • ADR-0008 is amended, not superseded: email keeps OTP (Supabase native) and account mail; ZeptoMail's transactional/promotional split maps 1:1 onto UTILITY/MARKETING and shares the consent discipline.
  • Ops burden accepted knowingly: billing runbook (no API), template quality management, webhook health monitoring — the watchdog-workflow pattern applies to a channel that is otherwise its own only witness.
  • A future BSP retreat stays cheap: everything provider-specific lives behind the dispatcher and the whatsapp_* registries; the envelope, consent ledger and admin surfaces are provider-neutral.