Skip to main content

Read the click-to-chat attribution funnel

GET /api/v1/ads/ctwa/attribution is the reading side of the click-to-chat (CTWA) loop. Setup — connecting Meta, launching a click-to-chat campaign, stamping the referral on inbound — lives in the ads activation guide and the QR guide. This page covers how to interpret the funnel once data is flowing: what it joins, what each field means, and how to reconcile it. For request/response schemas, see the Ads API reference.

1. What the funnel joins

The funnel rolls up three sources by the ad that drove them:
  • Entry points — every inbound message stamped with a Click-to-WhatsApp referral (source_id = the ad id). Because the stamp lives on inbound only, the funnel reads entries into chat, never outbound sends.
  • Orders — WhatsApp catalog orders from those contacts, credited first-touch to the ad that opened their chat. So the funnel attributes “which ad acquired this customer,” not “which ad last touched them.”
  • Ad spend — Meta ad spend for the same window, fetched over one batched ad-level insights call, so the ROAS numerator and denominator align.
This is why the funnel closes the acquisition loop: it answers “which ads brought customers in, how many, how much revenue they generated, and the return on that spend.”

2. Source metrics

The report-level totals (total_entry_count, total_unique_contacts, total_attributed_revenue_cents, spend, and overall ROAS/ROI) sit alongside a per-ad ads[] list sorted best-earners first, then busiest by entry volume. window_days (1–365, default 30) bounds the lookback. The same window_days also drives the Meta spend fetch — so a tighter window answers “did last week’s ads drive this” and a longer window captures nurture, with numerator and denominator always aligned to the same period.

3. Proven against a fixture

The funnel’s correctness is pinned by a paired regression test that runs the route against a fixture of referral and order rows, asserting the first-touch attribution, the currency-unconvertible surfacing, and the ROAS math. That’s what lets you trust that a renamed or dropped column becomes a caught error at the database boundary rather than silently empty attribution — the read validates each row against a schema before aggregating. You don’t need to run anything; this is why the funnel reconciles to your ledger instead of drifting.

4. How to read it in the dashboard

Call it directly:
  • No connected Meta ad account? You still get entry volume, customers, and revenue — ROAS reads unavailable (spend_available: false) rather than a fabricated return. Connect under Settings → Channels to light spend up.
  • ROAS is revenue/spend, not margin. A 4.2 ROAS is 4.20backper4.20 back per 1 of ad spend; roi_pct is the same ratio as a percentage of spend.
  • Multi-currency folds foreign-order revenue into the reporting currency via the shared FX source; anything unconvertible is shown separately. reporting_currency echoes what every figure is denominated in.

5. Reading vs setup

The ads activation guide walks the full loop — connect Meta, activate a CDP segment, launch the click-to-chat campaign, and post conversions back. This page is the interpretation side once that setup exists. The QR guide covers ad-free click-to-chat entry (print/web QR codes) — those scans don’t carry a CTWA referral stamp, so they won’t appear in this ad-attribution funnel. Use the right guide for the side you’re on.

6. Troubleshooting

  • No entry points in the funnel. The referral stamp lives on inbound click-to-chat messages only. If CTWA customers are chatting but the funnel is empty, the inbound webhook isn’t stamping the referral — check your WhatsApp channel webhook configuration, since that’s where the stamp is recorded.
  • 403 on the call. Reads are open to any authenticated role; if you see 403, the API key lacks an authenticated role — issue a key with the right role.
  • CHANNEL_UNAVAILABLE. Connect a Meta ad account first (POST /ads/connect), then re-read /ads/status.
  • ROAS null. cost_status: "no_spend" means no spend was returned for the window — either the ad account isn’t connected or Meta returned no ad-level insights for those ad ids in that period.

See also