DEV Community

Michael Strauss
Michael Strauss

Posted on

Attribute the form submit and the click-to-call, not just the pageview

Author: Michael Strauss

Disclosure: I work on Venbit Intelligence, which is mentioned below.

If you ship a marketing site with a contact form and a tel: link, pageview analytics will tell you that someone arrived. It will not tell you which ad, keyword or search produced the lead. The conversion lives in the inbox or the phone log, and unless you join that event to the first visit, the trail stops at the landing page.

This is a tracking problem, not a dashboard-taste problem. The pattern is straightforward: persist the first-touch source, then attach it when the form is submitted or a click-to-call link is tapped.

How attribution works in the browser

Four signals do most of the work.

  • UTM parameters (utm_source, utm_medium, utm_campaign, and the rest) are the labels you put on the URL for mailers, posts and ads you control.
  • document.referrer is the previous page, when the browser still sends it. Organic search, some social visits and many sites show up here. A lot of AI assistants and in-app browsers send a blank referrer, so "direct" is often a bucket of unknowns unless you have another clue.
  • Click IDs such as gclid (Google Ads) and fbclid (Meta) arrive on the landing URL when someone clicks an ad. Store them on the first hit. They are how you join a later lead back to the ad platform.
  • The conversion events themselves are submit on a form and a click on a[href^="tel:"]. Those are the moments you stamp the stored source onto the lead.

First touch is the usual rule for "which campaign originally brought this person." Capture it once, on the first landing, and do not overwrite it if they come back later through a reminder email.

A small first-touch pattern looks like this:

const KEY = "first_touch";

function firstTouch() {
  const saved = localStorage.getItem(KEY);
  if (saved) return JSON.parse(saved);

  const q = new URLSearchParams(location.search);
  const touch = {
    landing: location.pathname + location.search,
    referrer: document.referrer || "",
    utm_source: q.get("utm_source") || "",
    utm_medium: q.get("utm_medium") || "",
    utm_campaign: q.get("utm_campaign") || "",
    gclid: q.get("gclid") || "",
    fbclid: q.get("fbclid") || "",
    at: new Date().toISOString(),
  };
  localStorage.setItem(KEY, JSON.stringify(touch));
  return touch;
}

function attachHiddenFields(form, touch) {
  for (const [key, value] of Object.entries(touch)) {
    const name = "attr_" + key;
    let input = form.elements[name];
    if (!input) {
      input = document.createElement("input");
      input.type = "hidden";
      input.name = name;
      form.appendChild(input);
    }
    input.value = value;
  }
}

firstTouch();

document.querySelectorAll("form").forEach((form) => {
  form.addEventListener("submit", () => attachHiddenFields(form, firstTouch()));
});

document.addEventListener("click", (event) => {
  const link = event.target.closest('a[href^="tel:"]');
  if (!link) return;
  const body = JSON.stringify({ type: "tel", href: link.href, ...firstTouch() });
  if (navigator.sendBeacon) navigator.sendBeacon("/lead-tap", body);
});
Enter fullscreen mode Exit fullscreen mode

This is a teaching sketch, not a production library. You still need a server that stores the payload with the lead, a plan for consent in your jurisdiction, and a way to recognise ChatGPT and other AI-assistant referrals when the referrer is missing or generic. Do not treat localStorage as a full identity graph, and do not log phone numbers into a public endpoint.

Why a lot of analytics tools still miss the lead

Revenue-first tools such as DataFast are built to join website traffic to a payment in Stripe or a similar processor. That is the right job when the conversion is a checkout.

It is the wrong job when the conversion is a form send or a phone tap. Those events never hit the payment provider. A privacy-first traffic counter in the Plausible mold will show you sessions and sources, and still leave the call and the form unjoined. A chatbot that only holds a conversation has the same hole: the lead never carries the campaign.

Local businesses add one more requirement. Map-pack rankings have to be checked from the city the business serves. A national rank is the wrong number for a company that wins work within a few miles.

What to look for, or what to build

If you are wiring this yourself, the checklist is short. Persist first-touch UTMs, referrer and click IDs. Stamp them onto form submits and tel: taps. Keep AI-assistant traffic out of a vague "direct" bucket when you can identify it. If you also need local rank checks or an on-site agent that captures the lead, that is product work on top of the snippet above.

If you would rather use a product that already does the lead-side job, Venbit Intelligence ties each form send and phone tap to the ad, keyword or search that produced it, across twelve ad platforms. It shows traffic from AI assistants such as ChatGPT, checks Google rankings (map pack included) from the city the business serves, and includes Site Agent, an AI chatbot that answers by chat or voice and sends the lead to the inbox.

It is not a checkout-revenue tool. Use it, or a tool like it, when the event you care about is the lead.

A plain way to choose

If you sell online and the question is which channel produced the payment, start with a revenue-attribution product.

If the site converts on a call or a form, attribute those events. The snippet above is the core idea. Judge any tool on whether it actually joins them, covers the ad platforms you buy, and keeps AI traffic visible.

Pick the conversion you actually have, then measure that.

About the author: Michael Strauss

Top comments (1)

Collapse
 
rushdeploy profile image
Rishabh Mishra •

Good framing — the conversion lives in the inbox, not the dashboard. One thing I'd add: store the landing page path with the first-touch record too, since for small sites the page that converted matters as much as the channel.