# Рекламна атрибуція

Рекламний клік доходить від сторінки до угоди в CRM і повертається в Google Ads

> Full REST + WebSocket API reference: https://api.happ.tools/reference

Відвідувач натискає оголошення, читає кілька сторінок, відкриває чат і залишає заявку. У CRM з'являється угода — і в ній немає нічого про те, яка кампанія за цей візит заплатила. Тепер Happ веде клік через увесь ланцюг: сторінка → чат → угода → назад у рекламну систему.

Вмикати нічого не треба. Будь-який віджет із актуальним сніпетом починає збирати дані з наступного перегляду сторінки.

## Що збирається [#що-збирається]

На першій сторінці відвідувача скрипт віджета зчитує рекламні параметри з URL і зберігає їх на вашому ж домені:

| Група             | Параметри                                                                       |
| ----------------- | ------------------------------------------------------------------------------- |
| Google            | `gclid`, `gbraid`, `wbraid`                                                     |
| Meta              | `fbclid`                                                                        |
| TikTok, Microsoft | `ttclid`, `msclkid`                                                             |
| Мітки кампанії    | `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`, `utm_id` |

Разом із ними — сторінка входу і реферер.

Зберігаються два дотики, а не один:

* **перший** — кампанія, яка привела цю людину;
* **останній** — те, що вона натиснула, повертаючись; саме йому рекламні системи приписують конверсію.

Параметри переживають переходи: людина може зайти за рекламним посиланням, погортати п'ять сторінок із чистим адресним рядком і відкрити чат через годину — кампанія все одно буде відома. Візит без рекламних параметрів ніколи не затирає збережене, тож клієнт, який повернувся напряму, не перетворюється на «без джерела».

Розмови з реклами Click-to-Messenger та Click-to-Instagram Direct несуть свій рекламний контекст так само — з ідентифікатором оголошення та його назвою замість click id.

## Куди це потрапляє [#куди-це-потрапляє]

### У картку CRM [#у-картку-crm]

Кожна CRM-інтеграція, яка створює ліди чи угоди, дописує походження в коментар картки:

```
--- 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
```

Рядки «ключ: значення» — навмисно: їх читає менеджер очима, і так само їх розбирає скрипт, формула в таблиці або сценарій n8n, не просячи в нас вивантаження.

Асистента про ці дані не питають узагалі. Click id — це сотня беззмістовних символів; модель, якій доручили їх переписати, обріже або вигадає правдоподібні. Хибний click id гірший за відсутній: він вивантажиться в Google як чужа конверсія. Тому платформа підставляє його на сервері, поза розмовою.

**NetHunt** додатково показує кожне поле як параметр для зіставлення — `attribution_gclid`, `attribution_utm_campaign`, `attribution_note` та інші, — тож у налаштуваннях інтеграції ви наводите кожен на свою колонку.

### У ваш вебхук [#у-ваш-вебхук]

Якщо підключена інтеграція **Вебхук чатів**, у кожному повідомленні тепер їде об'єкт `attribution`, а в момент створення заявки надходить окрема подія:

```json
{
  "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` не змінюється при повторах — дедублікуйте за ним. Якщо відвідувач сконвертувався на пізнішій кампанії, перший дотик їде поруч, у полі `attribution.first`.

`phone` потрібен для [Enhanced Conversions for Leads](https://support.google.com/google-ads/answer/13258081): вони зіставляються за хешем номера і взагалі не потребують click id — запасний шлях для візитів, де `gclid` до URL не дійшов.

Тіла без поля `event` — це звичайні повідомлення, як і раніше.

### На сторінці, для GTM і GA4 [#на-сторінці-для-gtm-і-ga4]

Коли заявку створено, віджет кладе подію в `dataLayer` вашої сторінки:

```js
{
  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_..."
}
```

Це рахує саме заявку, а не відкриття чату: чат відкриває кожен цікавий відвідувач, і конверсія, визначена так, нічого не дає оптимізації. Подія дедублікується в браузері, а `happ_event_id` збігається з `eventId` із вебхука — серверна копія тієї ж конверсії зводиться з нею.

У GTM: тригер **Custom Event** на `happ_lead_created`, на ньому — тег GA4 або конверсія Google Ads, а `happ_event_id` передайте як ідентифікатор транзакції, щоб два джерела порахувалися один раз.

## Як замкнути коло з Google Ads [#як-замкнути-коло-з-google-ads]

1. Візьміть угоди, які CRM позначила як виграні.
2. Для кожної візьміть `gclid` із блоку атрибуції (або з колонки, на яку ви його зіставили).
3. Завантажте їх як [офлайн-конверсії](https://support.google.com/google-ads/answer/2998031) — зі значенням і часом конверсії.

Для угод без click id — відвідувач із органіки, iOS-візит, де `gclid` не з'явився — використовуйте Enhanced Conversions for Leads за номером телефону.

## Обмеження [#обмеження]

* Параметри зберігаються в `localStorage` браузера на вашому домені. Хто очистив дані сайту, змінив пристрій або сидить в приватному вікні — приходить як новий візит; перший дотик, уже записаний у його розмові, від цього не затирається.
* Голосовий дзвінок із віджета поки не доносить атрибуцію до CRM — чат доносить.
* Атрибуція відображає те, що було в URL. Якщо реклама веде на сторінку з редиректом, який зрізає query-рядок, збирати нічого — перевірте, що ваші редиректи його зберігають.
