
# Ad click attribution for WhatsApp

If you run Meta or Google ads to a page on your own website, and that page has a WhatsApp button, <span data-t="appName">DM Champ</span> can tell you which ad click each WhatsApp conversation came from. You paste one small script on your website. When a visitor taps your WhatsApp button, the click is remembered and matched to the first message they send you. The contact then shows the campaign it came from, and the click data travels with the contact into webhooks, the API and, if you want, back to Meta.

This works for any page with a WhatsApp button: a landing page, a booking page, a price page or a quiz that ends in "Send my answers on WhatsApp".

> This page is about ads that send people to **your website** first. If your ads open WhatsApp directly (Meta's "Click to WhatsApp" ads), that is covered automatically, without a script, in [Click-to-WhatsApp Ad Attribution](click-to-whatsapp-attribution.md).

---

## Set it up

1. Go to **Settings → Ad click tracking** (Integrations group).
2. Click **Turn on and get my script**. Your account gets its own site key and a one line script.
3. Copy the script and paste it just before the closing `</body>` tag on every page that has a WhatsApp button:

{% code overflow="wrap" %}
```html
<script src="https://api.dmchamp.com/v1/ad-visit/YOUR_SITE_KEY.js" async></script>
```
{% endcode %}

4. Optional: under **WhatsApp number**, type the number your buttons link to. Only links to that number are then tracked. Leave it empty to track every WhatsApp link on the site.
5. Choose the wording of the enquiry number (see below) and click **Save**. You do not need to change the script on your website after a settings change.
6. Test it: open your page from a link with a campaign tag on it, for example `https://yoursite.com/?utm_source=test&utm_campaign=setup-check`, tap your own WhatsApp button and send the message. Open that contact in Chats: the contact panel shows **Came from an ad click** with the campaign.

The site key is already inside the script. It is not a password, so it is fine on a public page.

::: master-only
**Agencies:** every client account has its own site key and script, so each client's website reports into its own account. You can hide the section for a client with menu visibility, like any other settings section.
:::

---

## What the script does

- **When the page loads**, it keeps the ad click ids and campaign tags (the `utm_` parameters) from the page address in the browser tab's session storage, so they are still there if the visitor looks at another page first. It sets no cookie, loads nothing else and sends nothing.
- **When the visitor taps a WhatsApp link** (`wa.me`, `api.whatsapp.com/send` or `whatsapp://send`), it adds the enquiry number to the message, records the visit and lets the tap continue to WhatsApp as normal.

Nothing is sent to <span data-t="appName">DM Champ</span> before the tap.

---

## The enquiry number

A short number, like the reference a shop or clinic gives you, is added to the visitor's WhatsApp message. It is how <span data-t="appName">DM Champ</span> knows exactly which ad click belongs to which conversation. A message looks like this:

```
Hi, I would like to know more.
Enquiry no. 4827
```

- **Wording:** "Enquiry no." is the default. You can use any one line of text that sounds natural for your business, such as "Booking ref" or "Ref". No digits.
- **Where:** **On its own last line** (the default) or **At the end of the text**. The settings page shows a live preview.
- **Length:** 4 digits, or 5 when your site is very busy.
- **Your AI agent never sees it** and never repeats it. The inbox shows the full message, number included.

You can switch the enquiry number off. Matching then only works on the text of the message, so two visitors who send exactly the same text (for example the same quiz answers) cannot be told apart, and some clicks will not be matched.

---

## Your own quiz or button code

If your page builds the WhatsApp message itself (a quiz that puts the answers in the text, or your own button code), add `data-manual` to the script tag:

{% code overflow="wrap" %}
```html
<script src="https://api.dmchamp.com/v1/ad-visit/YOUR_SITE_KEY.js" data-manual async></script>
```
{% endcode %}

With `data-manual` the script no longer changes WhatsApp links by itself. Your own code calls it right before it opens WhatsApp, so each visit is recorded once:

- `window.ChatAttribution.text(finalText, number)` returns your text with the enquiry number added, and records the visit. Use the returned text in your WhatsApp link.
- `window.ChatAttribution.link(finalText, number)` does the same and returns a ready `wa.me` link with that text. Leave out the number to use the one under **WhatsApp number** in the settings.

{% code overflow="wrap" %}
```html
<script>
  function sendAnswers(answers) {
    var text = "Hi, my answers: " + answers.join(", ");
    if (window.ChatAttribution) {
      window.location.href = window.ChatAttribution.link(text, "+15551234567");
    } else {
      window.location.href = "https://wa.me/15551234567?text=" + encodeURIComponent(text);
    }
  }
</script>
```
{% endcode %}

The `else` branch keeps the button working if the script has not loaded, for example when a browser extension blocks it. A text that already carries a number the script issued is never changed a second time.

---

## What gets recorded

At the tap, the visit record holds:

- the ad click ids in the page address, such as `fbclid` (Meta) and `gclid` (Google)
- the `_fbc` and `_fbp` cookies, if your own Meta pixel set them on your site (the script reads them, it never sets them)
- the campaign tags (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`)
- the landing page, the page the visitor came from, the browser, and the message text
- the IP address and the time, added by <span data-t="appName">DM Champ</span> when the visit arrives

Visit records are kept for **90 days**. When a visit is matched, a copy of its click data is stored on the contact and stays with the contact until you delete it.

**Your privacy notice:** the script runs on your website, so your website's privacy notice should mention it: what it records (the list above) and that it does so when a visitor taps a WhatsApp button.

---

## How matching works

When a contact's first message arrives, <span data-t="appName">DM Champ</span> looks for a visit from the **15 minutes** before it:

1. **By enquiry number first.** A message carrying a number the script issued is matched to that visit.
2. **Otherwise by identical text.** A message with exactly the same text as one visit is matched to it. When two different visitors sent the same text inside the window, neither is matched, on purpose, so a contact is never credited to the wrong ad.

A later message that carries the enquiry number is also matched, as long as the contact has no match yet.

Limits:

- **First match wins.** Once a contact has a match it is never overwritten.
- **WhatsApp only:** the WhatsApp Business API and WhatsApp Web channels.
- **15 minutes:** a visitor who taps the button but sends the message much later is not matched.
- **Going forward only:** contacts from before you installed the script have no match.

---

## Where you see it

- **Contact panel:** **Came from an ad click** with the campaign or source and the date. Hover for the details.
- **Webhooks:** `contact.ad_attribution` in [agent tag webhooks](webhooks.md#contact-tags-updated-webhook) and in every webhook subscription event that carries a contact.
- **API:** `ad_attribution` on the contact list and `adAttribution` on a single contact, in the Contacts section of the API reference.
- **MCP tools:** the same object on every contact they return.

The value is `null` when the contact has no match.

| Field | What it is |
|---|---|
| `visit_at` | When the visitor tapped the WhatsApp button. |
| `matched_at` | When the visit was matched to the contact. |
| `match_method` | `reference` (matched by enquiry number) or `text` (matched by identical message text). |
| `reference` | The enquiry number, or `null` when it was off. |
| `fbc` / `fbp` | The Meta click and browser cookies from your own Meta pixel, if present. |
| `fbclid` | The Meta ad click id from the page address. |
| `gclid` / `gbraid` / `wbraid` | The Google ad click ids from the page address. |
| `utm_source` / `utm_medium` / `utm_campaign` / `utm_content` / `utm_term` | The campaign tags from the page address. |
| `landing_url` | The page the visitor landed on. |
| `referrer` | The page the visitor came from. |
| `client_ip` | The visitor's IP address. |
| `user_agent` | The visitor's browser. |
| `site_key` | The site key of the script that recorded the visit. |
| `visit_id` | The id of the visit record. |

---

## Send results to Meta (optional)

You can let <span data-t="appName">DM Champ</span> report outcomes back to your Meta ad account, so Meta learns which ads bring real leads and sales and optimises toward them. This uses your own Meta Conversions API details.

1. In Meta Events Manager, find your pixel (also called dataset) ID under Data sources, and create an access token under Settings, Conversions API, **Generate access token**.
2. In **Settings → Ad click tracking**, under **Send results to Meta (optional)**, enter the **Pixel / dataset ID** and the **Access token**. While testing you can add the **Test event code** from Meta's Test events tab; clear it when you are done. Click **Save Meta details**.
3. Open the agent editor, go to the **Tags** tab and open a tag. Under **Send to Meta**, choose the event: **Lead**, **Contact**, **Schedule**, **Purchase** or a custom name. Optionally add a value and a currency.

From then on, whenever that tag is applied to a contact (by the AI, by hand, through the API or by an automation), <span data-t="appName">DM Champ</span> sends the event to Meta with the contact's stored click data (`fbc`, `fbp`, IP address, browser, landing page) plus their phone, email and name, hashed, so Meta can link it to the ad. A contact without a match still sends the event, with only the hashed phone, email and name.

- **No double counting:** each event has a fixed id made of the contact id and the tag name. If your CRM sends the same event to Meta with the same id, Meta keeps only one.
- **Meta's attribution window:** Meta only credits an event to an ad inside its attribution window, which is 7 days after the click by default. A deal that closes weeks later will not count on its own, so also send an earlier event, for example a tag for the booked or paid consultation.

---

## FAQ

**Does this work if my ad opens WhatsApp directly?**
Those ads do not pass through your website, so the script never runs. Meta sends the ad details with the first message instead, see [Click-to-WhatsApp Ad Attribution](click-to-whatsapp-attribution.md).

**Does the script slow down my website or set cookies?**
It loads in the background (`async`), sets no cookie and loads nothing else. It only sends something when a visitor taps a WhatsApp button.

**Can I track Google Ads as well as Meta ads?**
Yes. Google click ids (`gclid`, `gbraid`, `wbraid`) and any campaign tags are recorded and shown on the contact. Sending results back is available for Meta only.

**Why did a contact not get a match?**
The most common reasons: the message arrived more than 15 minutes after the tap, the enquiry number was removed from the message, the enquiry number is off and another visitor sent the same text, or the page does not have the script.

**My website links to two WhatsApp numbers. Can I track only one?**
Yes. Type that number under **WhatsApp number** in the settings. Leave it empty to track every WhatsApp link.

**Can I change the wording later?**
Yes. Changes apply to your website straight away; you do not need to paste the script again.
