Skip to Content
How It Works

How It Works

AttributionHub works in four stages on every page load: detect, classify, store, and populate.

The Attribution Pipeline

Stage 1: Detect

When a page loads, the script reads:

  • URL parametersutm_source, utm_medium, utm_campaign, utm_content, utm_term, and any custom fields
  • Click IDsgclid (Google), msclkid (Microsoft), fbclid (Meta), ttclid (TikTok), li_fat_id (LinkedIn), and more
  • Referrer — the document.referrer URL
  • Landing URL — the current page URL and path

Stage 2: Classify

Using the detected signals, the script classifies the visit into one of 20+ channel groups using a priority-based rule system:

  1. Check for explicit channel signals (e.g., utm_medium=cpc with a search source = “Paid Search”)
  2. Check for click IDs (e.g., gclid present = likely Google Ads)
  3. Match the referrer against known domains (200+ search engines, 80+ social networks, 18+ AI assistants)
  4. Fall back to medium-based classification
  5. Default to Direct (no referrer, no UTMs) or “Referral” (unknown external referrer)

Stage 3: Store

The classified data is saved as a touch snapshot in localStorage. Three snapshots are maintained:

Touch TypeKey PrefixBehavior
First Touchah_ft_The first non-direct visit. Locked permanently once set. Never overwritten.
Latest Touchah_lt_Updated on every page load, including direct visits.
Latest Non-Directah_lnd_Updated only when the visit comes from an external source. Preserved across direct revisits.

Additional stored values:

  • ah_visitor_id — a unique UUID generated on the first visit
  • ah_touch_count — number of non-direct visits (incremented only on external traffic)

Stage 4: Populate

The script discovers forms on the page and fills matching hidden fields with attribution data. See Conversion Tracking for the full form population architecture, supported platforms, and field mapping details.

Touch Snapshot Lifecycle

Here is how the three touch types behave across multiple visits:

Visit 1: Google Ads click first = { channel: "Paid Search", source: "Google" } latest = { channel: "Paid Search", source: "Google" } latestND = { channel: "Paid Search", source: "Google" } Visit 2: Direct (typed URL) first = { channel: "Paid Search", source: "Google" } -- unchanged latest = { channel: "Direct", source: "Direct" } -- updated latestND = { channel: "Paid Search", source: "Google" } -- preserved Visit 3: LinkedIn organic post first = { channel: "Paid Search", source: "Google" } -- unchanged latest = { channel: "Organic Social", source: "LinkedIn" } latestND = { channel: "Organic Social", source: "LinkedIn" } Visit 4: Direct (bookmark) first = { channel: "Paid Search", source: "Google" } -- unchanged latest = { channel: "Direct", source: "Direct" } -- updated latestND = { channel: "Organic Social", source: "LinkedIn" } -- preserved

Direct visits update the latest touch but intentionally do not overwrite the first or latest-non-direct touches, preserving the original marketing source.

Deep Dives