Zernio
APIFor you
Zernio
APIFor you
Platforms

WhatsApp

Overview

Set up

Connection & setupPhone NumbersSandbox

Messaging

InboxTemplatesBroadcastsFlowsGroup chatsContactsMeta Business Agent

Calls

Calling

Ad types

Click-to-WhatsApp ads

Reference

Pricing & costsMedia & limits
Dashboard
OverviewPlatformsAPI ReferenceResources
WhatsApp

Click-to-WhatsApp ads

Capture the click id Meta attaches to a Click-to-WhatsApp conversation and send conversion events back to Meta so they attribute to the ad.


By the end of this page, a purchase or lead that happened inside a WhatsApp conversation is attributed to the Click-to-WhatsApp (CTWA) ad that started it.

This page covers the WhatsApp side: the click id Meta attaches and the conversion events you send back.

Before you start

You need a connected WhatsApp account whose token carries whatsapp_business_manage_events, and a CTWA ad.

A CTWA ad is a Meta ad that opens a WhatsApp conversation with your business instead of a website. Create one with POST /v1/ads/ctwa.

Step 1: Zernio captures the click id

When someone reaches your number from a CTWA ad, Meta attaches a referral object to the first inbound message of the conversation. It holds ctwa_clid, Meta's click id for WhatsApp.

Zernio's webhook handler stores it on the conversation under metadata:

{
  "metadata": {
    "ctwa_clid": "AbCdEfGhIjKlMn0pQrStUvWxYz",
    "ctwa_captured_at": "2027-01-01T08:18:44.991Z",
    "ctwa_source_id": "120000000000000000",
    "ctwa_source_url": "https://fb.me/...",
    "ctwa_headline": "Chat with us on WhatsApp",
    "ctwa_body": "Replies in minutes",
    "ctwa_media_type": "image",
    "ctwa_image_url": "https://scontent.xx.fbcdn.net/...",
    "ctwa_source_type": "ad"
  },
  "adAttribution": {
    "adId": "120000000000000000",
    "name": "Spring CTWA",
    "synced": true,
    "campaignId": "120000000000000100",
    "campaignName": "Spring leads",
    "adSetId": "120000000000000200",
    "adSetName": "Spain 25-44",
    "adAccountId": "act_1234567890",
    "adAccountName": "Acme ads",
    "platformPostId": null,
    "isExternal": true
  }
}

adAttribution names the ad, campaign and ad set from the ads Zernio syncs for you. When the ad is not synced, synced is false and name falls back to Meta's headline. The same object arrives on the conversation.started webhook, and message.received carries it as metadata.ad next to the verbatim metadata.referral.

Capture is one-shot. Once ctwa_clid is set, later messages from the same person never overwrite it, because Meta emits referral only on the first message after a click.

Step 2: Provision the dataset

Conversion events that happen inside the WhatsApp conversation go back to Meta with action_source = business_messaging, so they attribute to the CTWA ad.

This is separate from Meta Ads' Conversions API for web pixel events.

Meta needs a dataset linked to the WABA before it accepts events.

Call POST /v1/whatsapp/dataset with accountId. Zernio creates the dataset and stores its id on the account as metadata.metaCapiDatasetId.

A WABA owns at most one CTWA dataset, so the call is idempotent. A second call returns the same id with created: false.

import Zernio from '@zernio/node';

const zernio = new Zernio();
const accountId = '66b2e19d8c3f5a7e9d0b1c2d';

const { data: dataset } = await zernio.whatsapp.createWhatsAppDataset({
  body: { accountId }
});

console.log(dataset.datasetId, dataset.created);

Response (200):

{
  "datasetId": "987654321098765",
  "created": true
}

GET /v1/whatsapp/dataset?accountId=... returns the stored id without calling Meta (datasetId: null until provisioned). Use it to show whether tracking is set up on an account.

If Meta rejects the call with (#100) Invalid parameter, the WABA has usually not finished business verification. Complete it in Meta Business Manager and try again.

The endpoint returns 422 with Meta's raw error, so other causes (region restrictions, app-level disablement) are visible.

A token minted through Embedded Signup already carries whatsapp_business_manage_events. A System User token you mint for the credentials flow must include it yourself.

Your own dataset. To use a dataset you already own instead, for example in another Business Manager, skip the provisioning call. Set metadata.metaCapiDatasetId on the account.

The WhatsApp account's token must then be able to reach that dataset. A WABA's System User token is scoped to the WABA's own Business Manager and cannot post to a pixel owned by a different Business (Meta returns code 100).

Share the dataset with the WhatsApp app's Business in Meta Business Manager, or use one already in the same Business.

Step 3: Send a conversion event

Call POST /v1/whatsapp/conversions with accountId, eventName, a stable eventId and the conversationId.

Zernio then:

  • replays the captured ctwa_clid on the event
  • forwards the WABA id as the per-channel attribution identifier
  • hashes email and externalId before they leave
const { data: conversion } = await zernio.whatsapp.sendWhatsAppConversion({
  body: {
    accountId,
    conversationId: '66c3d2ae7b4f6c8d0e1f2a3b',
    eventName: 'LeadSubmitted',
    eventId: 'lead_abc_123',
    value: 49.00,
    currency: 'USD'
  }
});

console.log(conversion.eventsReceived, conversion.eventsFailed);

Response (200):

{
  "platform": "metaads",
  "eventsReceived": 1,
  "eventsFailed": 0,
  "failures": [],
  "traceId": "AbCdEfGhIjKlMn0pQrStUv"
}

A 200 means the request reached Meta, not that Meta accepted the event. Check eventsFailed and failures[].

traceId is Meta's fbtrace_id for Events Manager.

Reuse the same eventId to suppress duplicates. Meta dedupes it against pixel events with the same id.

Identify the conversation either way:

  • conversationId, preferred: the conversation _id from List conversations.
  • phoneE164, fallback: digits only, no +. Zernio picks the most recent CTWA-attributed conversation for that phone on the account.

Supported event names

Meta's allowlist for business_messaging events is narrower than the pixel Conversions API:

EventUse for
LeadSubmittedForm filled, contact details captured
QualifiedLeadYour team qualified the lead
PurchaseCustomer completed a purchase (payment confirmed, invoice issued)
AddToCartCustomer added an item to the cart in the conversation
InitiateCheckoutCustomer started a checkout flow
ViewContentCustomer viewed a specific product or service
OrderCreatedAn order was placed
OrderCanceledAn order was canceled
OrderReturnedAn order was returned

Lead, the standard pixel name, is not accepted on business_messaging events. Use LeadSubmitted.

CompleteRegistration, Subscribe, Schedule, Contact, StartTrial, AddPaymentInfo, Search and SubmitApplication are rejected too.

Zernio verified the first five names live against Graph API v25.0. QualifiedLead, OrderCreated, OrderCanceled and OrderReturned come from Meta's Conversions API for Business Messaging list. None of them needs extra fields. Send value and currency with order events to report the order amount.

Zernio enforces the list at the request boundary. An unsupported name is a 400 before anything reaches Meta.

Attribution window and test mode

Meta's attribution window is 7 days from the click. An event whose ctwa_clid was captured more than 7 days ago still posts but does not attribute.

eventTime defaults to the time of the request.

Pass testCode: "TEST12345" at the request root to route events to the Test Events tab in Meta Events Manager, without touching production data.

Step 4: Check recent activity

Call GET /v1/whatsapp/conversions with accountId to list the most recent events sent. Use it for a "Conversions" panel in your own dashboard or for alerting on attribution sends.

const { data: recent } = await zernio.whatsapp.listWhatsAppConversions({
  query: { accountId, limit: 50 }
});

for (const e of recent.events) {
  console.log(e.timestamp, e.eventName, e.conversationId, e.eventsReceived, e.eventsFailed);
}

Response (200):

{
  "events": [
    {
      "timestamp": "2027-01-01T09:15:02Z",
      "eventName": "LeadSubmitted",
      "conversationId": "66c3d2ae7b4f6c8d0e1f2a3b",
      "eventsReceived": 1,
      "eventsFailed": 0,
      "traceId": "AbCdEfGhIjKlMn0pQrStUv",
      "durationMs": 412
    }
  ]
}

The feed comes from delivery logs, not an event store, so it covers about 30 days of log retention.

For a long-lived audit trail, persist each call to POST /v1/whatsapp/conversions on your side.

If it fails

A 422 from Step 3 means there is nothing to attribute:

{
  "error": "The resolved conversation has no captured ctwa_clid"
}

Three causes:

  1. The conversation did not start from a CTWA ad.
  2. It predates Zernio's capture.
  3. Meta omitted ctwa_clid from the referral. It does this on a minority of referrals on any number, most often WhatsApp Status placements.

The third one recovers on its own. Meta's automatic event identification can later detect a lead or purchase in the thread. Then whatsapp.automatic_event carries the clid, Zernio writes it back onto the conversation, and this call starts working for that thread.

Send the event only for conversations whose metadata.ctwa_clid is set. Re-read the conversation after that webhook rather than giving up on the first 422.

The other 422 is a missing metaCapiDatasetId on the account. Run Step 2.

A 404 means the conversationId does not exist.

Every error uses the envelope in error handling.

Related

  • Click-to-WhatsApp ads: create the ad with POST /v1/ads/ctwa.
  • Conversions API: web pixel events for the rest of your funnel.
  • WhatsApp webhooks: whatsapp.automatic_event, Meta's own lead and purchase detection in CTWA conversations.
  • Conversions API reference: every field, including contentIds, email and externalId.
  • Connection & Setup: the scope a System User token needs.
Was this page helpful?

Calling

Turn on WhatsApp Business Calling for a connected number, forward calls to a phone, SIP endpoint or AI voice agent, and place outbound calls with the customer's permission.

Pricing & costs

Which WhatsApp charges Zernio bills, which ones Meta bills to your WhatsApp Business Account, and how the sandbox, a dedicated number and your own number differ.

On this page

Before you startStep 1: Zernio captures the click idStep 2: Provision the datasetStep 3: Send a conversion eventSupported event namesAttribution window and test modeStep 4: Check recent activityIf it failsRelated