Skip to main content

Ads API

Connect a Meta Ad Account and run the full advertising loop from Orbit: create click-to-chat campaigns, forward conversions back to Meta for closed-loop attribution, turn Meta Lead Ads into contacts, and activate CDP segments as hashed Custom Audiences. Base path: /api/v1/ads Authentication: API key (X-API-Key) or session JWT. Every endpoint requires authentication. Roles: read endpoints (connection status, campaign list/detail, audience list) are open to any authenticated role. Every write — connecting, disconnecting, campaign create/update/delete, Conversions API calls, Lead Ads ingestion, and audience create/sync/delete — requires the owner or admin role. A single Meta Ad Account is stored per organization. Connect it first; the other endpoints return CHANNEL_UNAVAILABLE until a connection (and, for Conversions, a dataset ID) exists.

Connection

Connect via the hosted OAuth flow (/oauth/exchange, used by the in-app callback) or by posting an access token and ad account id directly (/connect). Tokens are encrypted at rest. The status response surfaces the selected ad account’s currency so budgets and spend render in the account’s own currency rather than defaulting to USD.

Campaigns

Click-to-chat campaigns drive WhatsApp, Messenger, or Instagram Direct conversations. New campaigns are created in PAUSED state so you can review them in Meta before they spend.

Conversions API

Forward business outcomes (lead, purchase, registration, appointment) from WhatsApp conversations that started on a click-to-WhatsApp ad back to Meta, closing the attribution loop. Email and phone are SHA-256 hashed before they leave Orbit — raw PII is never sent to Meta. Set a dataset_id once; the test and live endpoints both read it. A conversion must carry at least one identifier in user_dataphone, email, external_id, or ctwa_clid — for Meta to match it.

Lead Ads ingestion

Turn a Meta leadgen webhook batch into tenant contacts. Each lead’s instant-form answers are fetched from the Graph API and upserted through the same dedup, compliance, and required-field machinery as a CSV import. Leads with no email and no phone are skipped; one failed fetch never drops the rest of the batch. The response reports received, fetched, fetch_failures, skipped_no_contact, created, updated, and duplicates.

Custom Audiences

Activate a first-party CDP segment to Meta as a hashed customer-list Custom Audience for targeting or suppression, then keep it in sync as the segment recomputes. Membership is SHA-256 hashed before upload. A sync resolves up to 100,000 members per call; larger segments are truncated and the response sets truncated: true.

Recipes

Step-by-step request/response examples for the full advertising loop. Every response lands in the standard envelope (data + meta.request_id/meta.timestamp); the JSON below shows the data payload. The Node snippets call each endpoint through the SDK’s generic request helper — see the Node.js SDK page.

Create a click-to-chat campaign — POST /ads/campaigns

Create the campaign in one call. Orbit always submits it to Meta in PAUSED state, so you can review it before it spends; flip it to ACTIVE with PUT /ads/campaigns/{id} when you’re ready.
201:
The id is Meta’s campaign id — the same id you pass to GET/PUT/DELETE /ads/campaigns/{id}.

Set the Conversions API dataset — PUT /ads/conversions/dataset

Set once before posting any conversion. The dataset_id only accepts letters, digits, underscores, and hyphens.
200:

Verify CAPI delivery — POST /ads/conversions/test

No request body. Sends a test event to Meta so you can check Test Events in Events Manager before posting real traffic.
200:

Forward a conversion — POST /ads/conversions

When a click-to-WhatsApp conversation reaches a business outcome, replay it to Meta for closed-loop attribution. The ctwa_clid is the click id Meta attaches to the first inbound message of an ad-originated conversation — include it whenever you have it so Meta can join the conversion back to the ad. Every user_data value is hashed (SHA-256) before it leaves Orbit; raw PII is never sent to Meta. A conversion must carry at least one identifier — phone, email, external_id, or ctwa_clid.
200:
event_time is optional and defaults to the current time. Other outcomes map to Lead, CompleteRegistration, Schedule, or Contact; messaging_channel accepts whatsapp (default), messenger, or instagram.

Ingest a Lead Ads batch — POST /ads/leads/ingest

Replay one Meta leadgen page-webhook delivery and turn it into tenant contacts. The webhook carries only lead references — Orbit fetches each lead’s instant-form answers from Meta with your connected token, then upserts through the same machinery as a contacts import.
200:
Leads with no email and no phone land in skipped_no_contact; a lead whose Graph fetch fails lands in fetch_failures and never drops the rest of the batch. Re-submitted leads count under updated, not duplicates (upsert merge).

Create a Custom Audience — POST /ads/audiences

Create the Meta Custom Audience and link it to a CDP segment_id so later syncs resolve membership automatically.
201:

Sync segment membership — POST /ads/audiences/{audienceId}/sync

Upload (mode: "add", the default) or suppress (mode: "remove") the segment’s current membership. Identifiers are SHA-256 hashed before upload; raw email and phone never leave Orbit. Up to 100,000 members resolve per call — a larger segment is truncated and the response sets truncated: true. Omit segment_id to reuse the segment linked at create time.
200:
matched counts resolved members with at least one identifier, sent what Meta was handed, and num_received sums Meta’s per-batch acknowledgements — a small gap between them is normal (Meta dedupes). When truncated is true, only the first 100,000 members were pushed.

Delete a Custom Audience — DELETE /ads/audiences/{audienceId}

Removes the audience at Meta and drops Orbit’s stored link. Returns 204 No Content.

See also