Skip to main content

Ads hub: attribution, scope, and activation

The Ads section of the dashboard manages your Meta click-to-chat advertising loop: it connects one Meta Ad Account, runs campaigns whose destination is a WhatsApp, Messenger, or Instagram Direct conversation, activates your CDP segments as hashed Custom Audiences, ingests Meta lead-form submissions as contacts, and reads revenue attribution back against the entry ad. This page is the hub map — the three jobs the section owns, where each one lives in the dashboard, and which in-depth guide answers it. Endpoint schemas live in the Ads API reference; the attribution model itself is ads attribution and lead ingestion (concepts). Every surface below is a tenant-owned control — you connect your own Meta account, your segments define the audiences, and nothing renders until you put data in.

1. What the Ads hub owns

Three jobs, and the hub owns all three for exactly one network — Meta:
  • Attribution — capture which ad a contact entered from (the ctwa_clid click-id or Meta lead-form stamped on the inbound message), credit downstream orders and conversions to that ad first-touch, and roll entry counts, revenue, and Meta spend into per-ad ROAS over a 1–365 day window. The console deliberately has no attribution tab: read it from GET /ads/ctwa/attribution?window_days= and render it yourself — the API is the report.
  • Activation — push a first-party CDP segment to Meta as a hashed Custom Audience, for targeting or as a suppression list, and run the click-to-chat campaign against it. The same hub links a segment, syncs membership (add/remove), and manages the campaign lifecycle.
  • Scope — a deliberately narrow boundary: Meta click-to-chat and Meta Lead Ads only. No Google, TikTok, or other network connectors ship silently; the scope guide draws the boundary so you know exactly which ads questions the hub answers and which it declines.
Roles map onto the surfaces the way you would expect: all member seats read connection status, campaigns, audiences, and the attribution API; every write — connect/disconnect, campaign create/update/delete, audience create/sync/delete, conversions configuration/test/post, lead ingestion — needs owner or admin.

2. Connecting an ad account and ingesting leads

Two ways a lead enters, and the hub’s tiles lie along both paths:
  1. Connect — authorize one Meta Ad Account per organization over OAuth from Settings > Channels > Meta Ads; the console records the account’s billing currency so every spend and ROAS figure renders in that currency. Disconnect is a typed-DISCONNECT confirmation, because it stops live data flow. Every other Ads endpoint returns CHANNEL_UNAVAILABLE until a connection exists.
  2. Entry path A — click-to-chat. Create click-to-chat campaigns from the Ad Campaigns tab; new campaigns are created PAUSED so you review them in Meta before spending, then flip ACTIVE. The destination is always one of Meta’s conversation surfaces: WhatsApp, Messenger, or Instagram Direct. The first inbound message carries Meta’s referral block (ad id, ctwa_clid), which becomes the attribution anchor.
  3. Entry path B — lead-form ingestion. The Lead Ads tab pastes the leadgen webhook payload Meta sent your webhook endpoint; only leadgen changes are extracted, each leadgen_id is fetched from Meta’s Graph, and contacts are upserted through the same dedupe-consent-audit path as a CSV import. A lead with neither email nor phone is skipped, and per-lead failures never abort the batch. Lead-form entries land in the same attribution view as click-to-chat entries.
For the operational walk — OAuth steps, the PAUSED-at-create rule, the CAPI test-then-post loop, troubleshooting connect-state and match failures — use the Ads console guide. This page points; the console guide walks.

3. Attribution model examples — ROAS and lead-to-conversion

The attribution report is first-touch: the earliest captured referral fixes the contact’s entry ad, per-ad contact counts partition cleanly (summing across ads never double-counts), and orders placed before the ad tap are excluded. Two shapes the report reads: ROAS per ad. GET /ads/ctwa/attribution?window_days=30 returns per-ad rows — ad_id, entry_count, unique_contacts, attributed_revenue_cents, spend_cents, roas, roi_pct, cost_status — with Meta spend fetched over the same window so numerator and denominator align. Revenue in a currency with no caller-supplied FX rate surfaces in unconvertible_revenue_cents rather than blending at 1:1. When no spend is fetchable — no account connected, or the Meta spend read failed — the report still returns entry attribution with roas: null and cost_status: "no_spend", so the window arithmetic never silently mixes periods. Lead-to-conversion through CAPI. Entry is the first half; the report closes the loop because you forward outcomes back to Meta. POST /ads/conversions accepts any Meta standard event (Lead, Purchase, CompleteRegistration, Schedule, Contact, up to 64 characters) and requires at least one match key — the deterministic ctwa_clid, or a hashed phone/email/external_id. Without a match key, Meta cannot attribute the event and the API rejects with 422. Phone, email, and external id are SHA-256 hashed server-side before they leave; the ctwa_clid travels as-is, because it is Meta’s own click id. A deterministic join credits the entry ad; a hashed-identity join is probabilistic on Meta’s side, and some phone/email-only conversions land unattributed there. Run Conversions > Test event before live traffic — the recent-test-events panel logs only Meta-confirmed receipts, so a silent zero-received reject never becomes a false green. The worked attribution walk — entry counts vs. unique contacts, credited revenue, FX-ratable vs. unconvertible revenue, spend-joined ROAS — is in the concepts page. This section names the shapes; the concepts page works the arithmetic.

4. How scope governs activation

Scope is not a marketing footnote here — it decides which audiences and segments the hub activates:
  • Activation is segment-linked. The Audiences tab activates a CDP segment_id as a hashed Meta Custom Audience; syncing against a segment with no email/phone contacts ships an empty payload, because the API filters membership to rows with a contactable identifier before calling Meta. A sync pages contacts 10,000 per Meta call and caps at 100,000 members, reporting truncated: true when it cut the list — narrow the segment and re-sync.
  • Targeting and suppression are both in scope. A sync runs in Add members (targeting) or Remove members (suppression) mode; pair suppression audiences with your organization’s opt-out lists and check consent before a segment feeds an ad audience with the audience consent inspector. These controls are tenant-owned — consent to use a contact’s data on ad platforms sits on your side of the consent line.
  • The network boundary is explicit. Meta click-to-chat and Meta Lead Ads are in scope; Google, TikTok, and every other network are out of scope, and the scope guide says why — no other network exposes the deterministic ctwa_clid join, so folding one in would ship weaker attribution. Any new network lands as its own connector, announced on the changelog, and the scope page is updated the day it ships.
Before connecting any spend, build the segment in the CDP — the CDP segments guide covers the recipe — and size its filter with the segment-preview call so a 0-match filter never reaches spend. The full segment-to-campaign-to-attribution walkthrough is the Meta Ads activation guide.

5. Where the in-depth docs live

FAQ

Where do I start? Connect in Ads console walkthrough, then walk the loop end-to-end in Meta Ads: segment to campaign to attribution. This page rotates between them; use it as the map, not as the walkthrough. Why no Google or TikTok ads? Scope — read Ads: scope and feature boundaries. The console answers inside the Meta click-to-chat loop and declines outside it, because the deterministic click-id join only exists on Meta’s conversation surfaces. Where is the attribution tab? There is none. Per the Ads console walkthrough, attribution is API-only — GET /ads/ctwa/attribution?window_days= — so the console never re-derives what the API computes.