Skip to main content
NewNMC’s advertising guidelines for doctors are in force from 6 Oct 2026. See what to change, and get a free compliance auditNew NMC advertising rules for doctors: what to change
Paid Ads

Click-to-WhatsApp Into a CRM: Capturing the Referral ID for Closed-Loop Attribution

A technical walkthrough of capturing the ctwa_clid from the WhatsApp webhook, storing it in a CRM, and returning qualified-lead and purchase events to Meta without sending patient health data.

Click-to-WhatsApp Into a CRM: Capturing the Referral ID for Closed-Loop Attribution

This post is for the person who has to wire Click-to-WhatsApp ads into a hospital or clinic CRM: a marketing-operations lead, a CRM developer, or an agency analyst. It assumes you already run the ads and answer the chats, and that the reports show "conversations started" but not which ad produced a booked appointment. If you still need the set-up basics and the cost model, read our post on Click-to-WhatsApp ads for clinics first. This one stays in the plumbing.

The aim is a closed loop in four steps. Capture the identifier Meta sends when a chat starts from an ad. Store it against the right patient record. Update it as the lead moves through your pipeline. Send the milestones back to Meta so the ad system can optimise for qualified leads and not only for chats. Each step has failure modes, and most "attribution tools" hide them.

Field names and request shapes below come from Meta's documentation as it stood in October 2026. Meta changes API versions often, so verify against the linked pages and your own raw payloads before you build.

Step 1: What arrives in the webhook

When a person taps a Click-to-WhatsApp ad and sends the first message, the WhatsApp Business Platform delivers an incoming-message webhook. Where the chat came from an ad, the message object carries a referral object. Meta's messages webhook reference lists these fields:

FieldMeaning
ctwa_clidThe Click-to-WhatsApp click ID. Omitted for messages from an ad in WhatsApp Status
source_idID of the ad that produced the message
source_typeShown as ad for ad-originated messages in Meta's example
source_urlURL associated with the ad
headlineThe ad headline
bodyThe ad's primary text
media_typeimage or video
welcome_messageObject whose text holds the ad's greeting

In a parsed webhook the path is entry[].changes[].value.messages[].referral, next to the sender's from number, the message id and the timestamp. Log the raw JSON of the first few hundred inbound messages before you write a parser. Integrators have reported referral data being dropped by wrappers, which is easy to miss because nothing errors.

Two points save debugging time. The referral object appears on the message that opens the thread from the ad, not on every later message. And because the ctwa_clid is absent for Status ads, a missing click ID does not by itself mean the lead was organic.

Step 2: Design the storage

Do not put ctwa_clid in a single column on the contact. A patient can click three different ads over two months, ask about different services, and book on the third. Use a separate touch table.

TableKey fields
contactinternal id, WhatsApp number (normalised), consent status, first seen, source summary
conversationid, contact id, started at, entry point (ad, organic, QR, website button), service line, location
ad_referralconversation id, ctwa_clid, source_id, source_type, headline, body, media_type, received_at, raw JSON
stage_eventconversation id, stage, occurred at, actor, value, CAPI sent flag, CAPI sent at, response code

Rules worth enforcing in code:

  • One referral per conversation start. If a returning patient messages again without an ad, create a conversation with entry point organic, and do not overwrite the earlier referral.
  • Store the raw JSON. Field names or nesting will change, and you will want to re-parse historic data.
  • Normalise the number before matching. WhatsApp sends numbers without a plus sign. Match against your appointment system on a canonical format, or you will split one patient into two records.
  • Treat a missing referral as unknown. Do not label it organic. Reasons for absence include Status ads, a patient who messages the number saved from a previous click, a QR code, and a provider that strips the object.

If your chat sits behind a business solution provider, ask them in writing whether the referral object is passed through unchanged and whether they expose the click ID. Tools built for this purpose, such as WaCRS, pass it through to the CRM record, but test whichever one you use against your own phone.

Step 3: Map pipeline stages to events

The call centre or front desk moves a conversation through stages. Each stage that a human has verified is a candidate event to send back. A reasonable hospital mapping:

CRM stageMeaningEvent to send
Conversation startedInbound message from adNone; Meta already reports it
QualifiedRight service, serviceable city, reachable patient, not a spam or vendor messageQualifiedLead
Appointment confirmedSlot booked and acknowledged by the patientLeadSubmitted (or a custom event, see below)
Consultation fee paidPayment received at booking or at the visit deskPurchase with the fee as value

Meta's business messaging Conversions API documentation lists the supported event names, including LeadSubmitted, QualifiedLead, Purchase, RatingProvided and ReviewProvided. There is no appointment-specific event in that list, so decide on one convention, write it down, and keep the mapping in configuration, not in code. Check Events Manager after your first batch to confirm that the events were accepted.

Two cautions on what you send. First, send business outcomes and nothing clinical. Never put a diagnosis, procedure name, test result or symptom into an event parameter. Meta's Business Tools Terms restrict sharing sensitive information such as health data, and a hospital's service line can be sensitive by itself, so keep custom parameters generic. Second, the value on a Purchase should be the fee actually paid in the thread, not a projected procedure value.

Step 4: The Conversions API request

For WhatsApp, the documentation specifies the following for each event. The request goes to your dataset's events endpoint with these elements:

  • event_name: one of the supported names above.
  • event_time: when the stage change actually happened, in Unix seconds.
  • action_source: business_messaging.
  • messaging_channel: whatsapp.
  • user_data.whatsapp_business_account_id: your WhatsApp Business Account ID.
  • user_data.ctwa_clid: the stored click ID, unmodified.
  • custom_data: currency and value where relevant.

Meta states that the click ID comes from the referral object and must be stored with the conversation. The documentation also lists the permissions needed (whatsapp_business_management and whatsapp_business_manage_events, with advanced access, plus Marketing API access tier), so allow lead time if your app review has not been done.

Implementation details that decide whether it works:

  1. Deduplicate yourself. Meta's documentation says it does not deduplicate these events. Use a unique constraint on conversation id plus event name in your stage_event table, and make retries idempotent.
  2. Send close to real time. Use a queue that fires when the stage changes, with retries and a dead-letter list. Daily batch uploads delay learning for the ad system.
  3. Only send what happened in the thread. Meta says these events should represent interactions that occur in the messaging thread. A website form conversion belongs in web events.
  4. Do not send events with no click ID. An event that cannot be tied to an ad adds noise. Keep it in your CRM reporting.

Meta does not state a validity window for the click ID in the documentation above. Do not assume one, and do not hold events for weeks. Test with a staged click and watch for acceptance in Events Manager.

Want this applied to your practice? Get a free paid ads audit.

Book my free audit

Consent and privacy

The patient messaged you first, but that is not the same as consent for marketing messages or for every use of their data. Under India's DPDP framework, the notice and consent you rely on should cover why you hold the WhatsApp number and how it is used. Your front-desk script and your privacy notice should say that conversation outcomes may be used to measure advertising. Our compliance and opt-in management service covers the consent trail. Do not send phone numbers or names to Meta in these events when the click ID alone is enough.

Testing and reconciliation

  • Staged click test. Click your own live ad from a test phone, send a message, and confirm that the raw webhook contains a referral with the expected source_id. Then push the contact through the stages and confirm each event appears in Events Manager.
  • Match-rate report. Weekly, compare conversations started in Ads Manager with conversations in your CRM that have a click ID. A gap is normal; a gap that widens after a deployment is a bug.
  • Ad-level view. Join source_id to ad names through the Marketing API, so reports show cost per qualified lead by creative and not just by campaign.
  • Edge cases. Test a patient who sends two messages from two ads, a staff reply sent before the patient writes, and a number that appears in two conversations.

Failure modes we see most often

  • The click ID is stored but never sent. The capture works, the stage events are created, and the sender is switched off after a deployment. Alert on a gap between qualified leads with a click ID and events accepted.
  • Duplicate patients. Two number formats produce two contacts, and the click ID sits on the one the call centre never opens.
  • Stages moved in bulk. A coordinator clears the backlog on Friday by marking forty chats "appointment confirmed", and Meta receives forty events with the same timestamp. Send events when work happens, and restrict bulk stage changes.
  • Optimising on a weak event. If qualified-lead volume is very low, the ad system may have too little signal. Start with the most frequent verified event and move deeper later.

Where to start

  1. Log 200 raw inbound webhooks and measure what share carry a referral.
  2. Create the touch and stage tables above.
  3. Agree the stage-to-event mapping with the call-centre lead and write it down.
  4. Build the sender with idempotency and a retry queue.
  5. Run the staged-click test, then the weekly reconciliation.
  6. Review results by creative once there are enough qualified leads to compare.

For the wider campaign structure, see our Click-to-WhatsApp ads service and the WhatsApp for healthcare guide.

Filed underclick to whatsapp ads attributionctwa_clidWhatsApp Business API CRM integrationMeta conversions API WhatsAppclick to WhatsApp ads CRM trackingWhatsApp ad referral webhook

About the author

Co-Founder & CTO · Gurugram, India

Arush builds the systems the rest of the company runs on — the patient-acquisition pipelines, the AI chatbots, the analytics that tie a single click to a booked appointment. If a campaign reports a number, it's because something his team built is tracking it.

Free guide · 17 pages

The Patient Acquisition Blueprint

A 90-day plan from first search to booked appointment.

The Pioneers Brief — one essay, three benchmarks, one teardown, every Thursday. Subscribe free →

Free paid ads audit

Your next chapter starts with a conversation.

Three quick questions. We come prepared with a plan for your specialty and city.

  • A 30-minute call with a healthcare specialist
  • Your top three growth opportunities, in writing
  • Honest advice — even if that means not hiring us
  • No obligation and no hard sell
You’ll speak with a senior strategist
Consultations are hosted by Arush Thapar’s team.
Step 1 of 3Takes 30 seconds

What do you want to improve first?

Details stay privateNo obligationReply within 1 working day

Client portfolio · BTL & activations

Work relevant to click to whatsapp into a crm capturing the referral id for.

Explore client work →