Capture UTM parameters and ad click IDs in form submissions
Keep the campaign that brought a visitor, even after they’ve browsed three pages before filling in your form, and store it on the lead.
Updated
Your ad sends people to /pallets/48x40?utm_source=google&utm_campaign=ohio-pallet-buyers&gclid=Cj0K…. The visitor reads that page, clicks through to pricing, then to the contact page, and fills in the form. By then the address bar says /contact and the campaign is gone. The lead arrives with no idea where it came from.
The fix is to save the tracking values on the visitor’s first page, then send them with the form, whatever page it’s on.
What to keep
| Value | Where it comes from | Why it matters |
|---|---|---|
utm_source, utm_medium, utm_campaign, utm_term, utm_content | Your tagged links | Which campaign, ad and keyword |
gclid, gbraid, wbraid | Google Ads | Sending conversions back to Google |
msclkid | Microsoft Ads | The same, for Bing |
fbclid | Meta ads | The same, for Facebook and Instagram |
ttclid | TikTok ads | The same, for TikTok |
| Referrer | document.referrer | Organic search and links from other sites |
| Landing page | The first URL of the visit | Which page did the persuading |
Keep the first values you see and don’t overwrite them as the visitor moves around. That’s first-touch attribution: the campaign that started the visit gets the credit.
Save them on the first page
Put this in your site’s layout so it runs on every page. It only writes when nothing is saved yet, so later pages don’t wipe the landing values.
// attribution.js
const KEYS = [
"utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content",
"gclid", "gbraid", "wbraid", "msclkid", "fbclid", "ttclid",
];
(function saveFirstTouch() {
if (sessionStorage.getItem("attribution")) return;
const params = new URLSearchParams(location.search);
const saved = {
landing_page: location.pathname + location.search,
referrer: document.referrer || undefined,
};
for (const key of KEYS) {
const value = params.get(key);
if (value) saved[key] = value;
}
sessionStorage.setItem("attribution", JSON.stringify(saved));
})();
export function attributionFields() {
const saved = JSON.parse(sessionStorage.getItem("attribution") || "{}");
const fields = { _1db_current_page: location.pathname };
for (const [key, value] of Object.entries(saved)) {
fields[`_1db_${key}`] = value;
}
return fields;
}sessionStorage lasts for the browser tab’s visit. If your buyers often leave and come back days later before filling in the form, use localStorage with a date and expire it after 30 or 90 days, matching your ad platform’s attribution window.
Send them with the form
Add the saved values to the body you post. The _1db_ prefix tells 1DB these are attribution, not answers from the visitor.
import { attributionFields } from "./attribution.js";
const body = {
formSlug: "quote-request",
submission_id: submissionId,
...Object.fromEntries(new FormData(form)),
...attributionFields(),
};If your form posts to your own server route first, have the browser include these fields and pass them through unchanged.
What 1DB does with them
1DB recognizes these fields:
| Field | Stored as |
|---|---|
_1db_utm_source … _1db_utm_content | The lead’s UTM values |
_1db_referrer | Referrer |
_1db_landing_page | Landing page |
_1db_current_page | The page the form was sent from |
_1db_gclid, _1db_gbraid, _1db_wbraid, _1db_msclkid, _1db_fbclid, _1db_ttclid | Click IDs |
_1db_ga_client_id | Your Google Analytics client ID |
They’re kept on the lead, shown in the dashboard, and can go along when the lead is forwarded to a partner, so whoever works the lead knows which campaign it came from. 1DB also records the visitor’s device, browser and approximate location from the request itself, so you don’t need to send those.
Check it works
Open your site with made-up tags, like /?utm_source=test&utm_campaign=attribution-check, click to another page, and send the form. The lead in the dashboard should show test and attribution-check, with your home page as the landing page.
Want the form itself? Start with the Next.js contact form or a backend for a static site.