Meta Ads: segment to campaign to attribution
This guide walks the full advertising loop in Orbit from segment to attribution: connect a Meta Ad Account, activate a first-party CDP segment as a hashed Custom Audience, launch a click-to-chat campaign, and close the loop by forwarding conversion events back to Meta and reading attribution in Orbit. Each step links a capability page to the next, and this page covers the workflow; the Ads API reference has the endpoint schemas.1. What the Ads surface contains
The Ads surface groups five capabilities under one account connection (base path/api/v1/ads; the public names below are customer-facing endpoints, the internal route module is only the implementation):
- Connection — OAuth or token-based link to exactly one Meta Ad Account, encrypted at rest. The
statusendpoint reports the account currency so spend renders correctly. - Campaigns — create, list, update, and delete click-to-chat campaigns over WhatsApp, Messenger, or Instagram Direct. New campaigns are created
PAUSED. - Conversions API — forward business outcomes (lead, purchase, registration, appointment) back to Meta for attribution, with email and phone SHA-256 hashed before dispatch.
- Lead Ads ingestion — ingest a Meta
leadgenwebhook batch into tenant contacts through the same dedup and required-field machinery as a CSV import. - Custom Audiences — activate a CDP segment to Meta for targeting or suppression, hashed before upload.
2. Step 1 — build the audience segment
Decide who the ads should reach before connecting any spend. Build and refresh that segment in the CDP — the segment recipe is fully covered in CDP audiences: build segments. Two reminder points:- Base the segment on facts (attributes, events, computed traits) so a later sync references a
segment_id, not a stale export. - Set
auto_refreshon the segment so the Custom Audience re-sync below tracks membership without manual uploads.
POST /api/v1/contacts/segments/preview before saving, so a 0-match filter never reaches spend.
3. Step 2 — connect Meta and register the campaign surface
Connect one Meta Ad Account per organization, then verify:CHANNEL_UNAVAILABLE until a connection exists. Have the OAuth flow (in-app callback) or a direct token ready, and have owner/admin on the API key used, because connect is a write.
4. Step 3 — activate the segment and launch the campaign
Activate the segment as a Custom Audience, then create the click-to-chat campaign:ACTIVE after Meta review. A sync resolves up to 100,000 members per call and reports truncated: true when it cut the list — plan a second sync on larger segments. For the full campaign lifecycle (draft, dry-run, launch, holdout, ROAS), the campaign end-to-end guide carries the general pattern; this loop is the campaign-launch subset applied to ads.
5. Step 4 — read attribution back
Set the Conversionsdataset_id once, post outcomes from the click-to-chat conversation into Meta, and pull the attribution view that joins spend, referrals, and outcomes:
- Hashing before dispatch. Email and phone in
user_dataare SHA-256 hashed before they leave Orbit — raw PII never goes to Meta. - At least one identifier. A conversion without any of
phone,email,external_id, orctwa_clidcannot be matched by Meta and is rejected. - Test first.
POST /api/v1/ads/conversions/testsends a test event to the dataset before live traffic, so a misconfigureddataset_idis caught cheap. - Read back cross-checked. The
/ads/ctwa/attributionview joins captured click-to-chat entry points, orders, and ad spend into one report; reconcile it against the referrals funnel landing pages and the analytics funnel event log.
6. Cross-check the id and host boundaries
Three boundaries to keep clean when wiring this loop:- One account, one host. A single Meta Ad Account connects per organization, and the
statusendpoint returns the accountcurrency— do not parse spend as USD if the connection says otherwise. - Membership caps. A Custom Audience sync is bounded to 100,000 members per call; the response marks
truncated: trueand amodeofaddvsremovedecides whether the segment sync adds members or suppresses them. Re-run sync against a paginated segment when it truncates. - Attribution is inbound-only. The click-to-chat referral stamp is recorded on inbound messages only, so the attribution funnel reads entry points, not outbound sends. Outbound delivery of ads never leaves the tenant — only hashed identifiers and hashed event IDs do.
7. Troubleshooting
CHANNEL_UNAVAILABLEon every call. Connect first (POST /ads/connect), then re-read/ads/status.- 403 on a write. Reads are open to any role; writes need owner or admin — issue a key with the right role.
- Conversions rejected. Verify
dataset_idis set, at least one identifier is present inuser_data, and run/conversions/testbefore posting live events. - Sync returns
truncated: true. The segment exceeded the 100,000-member per-call window — split the segment or sync in successive paginated calls. - Attribution shows no entry points. The referral stamp lives on inbound click-to-chat messages only; check
window_days(1–365, default 30) and that the campaign actually reachedACTIVE.
See also
- Ads API reference — every endpoint that backs this loop
- CDP audiences: build segments — the segment recipe
- Campaign end-to-end — the general campaign lifecycle pattern
- Analytics funnel and referrals funnel — cross-check attribution