HappHapp Docs

Ad attribution

Keep the ad click that brought a customer all the way to the deal in your CRM and back to Google Ads

A visitor clicks your ad, reads a couple of pages, opens the chat, and leaves a request. Your CRM gets the deal — but with nothing on it that says which campaign paid for that visit. Happ now carries the click through the whole chain: page → chat → deal → back to the ad network.

Nothing to switch on. Any widget loaded with the current embed snippet starts collecting on its next page view.

What gets collected

On the visitor's first page, the embed script reads the ad parameters out of the URL and stores them on your own domain:

GroupParameters
Googlegclid, gbraid, wbraid
Metafbclid
TikTok, Microsoftttclid, msclkid
Campaign labelsutm_source, utm_medium, utm_campaign, utm_content, utm_term, utm_id

It also keeps the landing page and the referrer.

Two touches are kept, not one:

  • first touch — the campaign that introduced this person;
  • last touch — what they clicked on the way back, which is what the ad networks attribute a conversion to.

The parameters survive navigation: a visitor can land on the ad URL, browse five pages with a clean address bar, and open the chat an hour later — the campaign is still there. A visit carrying no ad parameters never overwrites what was stored, so a customer who returns directly to convert stays attributed to the campaign that won them.

Conversations that arrive from a Click-to-Messenger or Click-to-Instagram ad carry their ad context the same way, with the ad id and creative title in place of a click id.

Where it ends up

In the CRM record

Every CRM integration that creates leads or deals writes the origin into the record's comment:

--- Happ attribution ---
channel: widget
gclid: Cj0KCQjw...
utm_source: google
utm_medium: cpc
utm_campaign: raiden-brand
landing: /catalog
captured: 2026-09-19T08:12:44.000Z

Key-value lines on purpose: a manager reads them, and a script, a spreadsheet formula or an n8n flow parses them without asking us for an export.

The assistant is never asked to pass any of this. A click id is a hundred opaque characters, and a model told to copy one will truncate it or invent a plausible one — a wrong click id is worse than none, because it gets uploaded as someone else's conversion. The platform attaches it server-side, out of the conversation entirely.

NetHunt additionally exposes every field as a mappable parameter — attribution_gclid, attribution_utm_campaign, attribution_note and the rest — so you can point each one at a column of your own folder in the integration settings.

In your webhook

If you have the Chat webhook integration connected, every message body carries an attribution object, and a new event fires when a request is actually created:

{
  "event": "lead.created",
  "eventId": "6f2a...:8821",
  "chatId": "6f2a1c84-...",
  "companyId": "8fb075ae-...",
  "toolName": "keepincrm_create",
  "entityId": "8821",
  "entityType": "leads",
  "phone": "+380731234567",
  "createdAt": "2026-09-19T08:14:02.000Z",
  "attribution": {
    "channel": "widget",
    "clickId": "Cj0KCQjw...",
    "clickNetwork": "google",
    "utmSource": "google",
    "utmMedium": "cpc",
    "utmCampaign": "raiden-brand",
    "landingPage": "/catalog",
    "referrer": "https://www.google.com/",
    "capturedAt": "2026-09-19T08:12:44.000Z",
    "note": "--- Happ attribution ---\nchannel: widget\ngclid: Cj0KCQjw..."
  }
}

eventId is stable across retries — dedupe on it. When the visitor converted on a later campaign than the one that introduced them, the first touch rides along under attribution.first.

phone is there for Enhanced Conversions for Leads, which match on a hashed phone number and need no click id at all — the fallback for visitors whose gclid never made it into the URL.

Bodies without an event field are ordinary messages, as before.

On the page, for GTM and GA4

When a request is created, the widget pushes an event into dataLayer on the host page:

{
  event: "happ_lead_created",
  happ_event_id: "6f2a...:8821",
  happ_chat_id: "6f2a1c84-...",
  happ_entity_id: "8821",
  happ_entity_type: "leads",
  happ_widget_id: "wgt_..."
}

This counts the request, not the chat opening — every curious visitor opens the chat, and a conversion defined that way teaches the bidding nothing. The event is deduped in the browser, and happ_event_id matches the webhook's eventId, so a server-side copy of the same conversion reconciles against it.

In GTM: create a Custom Event trigger on happ_lead_created, use it for your GA4 event or Google Ads conversion tag, and pass happ_event_id as the transaction/event id so the two sources are counted once.

Closing the loop with Google Ads

  1. Take the deals your CRM marks as won.
  2. For each one, read gclid out of the attribution note (or out of the column you mapped it to).
  3. Upload them as offline conversions with the conversion value and time.

For deals with no click id — a visitor from organic search, an iOS visitor whose gclid never appeared — use Enhanced Conversions for Leads on the phone number instead.

Limits

  • Parameters are stored in the browser's localStorage on your domain. A visitor who clears site data, switches devices or browses in a private window arrives as a fresh visit; the first touch already recorded on their conversation is not overwritten by it.
  • A voice call started from the widget does not carry the attribution into the CRM yet — the chat does.
  • The attribution reflects what was in the URL. If your ads land on a page that redirects and drops the query string, there is nothing to capture; check that your redirects preserve it.

Help me solve it

Happ answers right away and hands over to a manager when needed.

On this page