Where the tracking script keeps a visitor's click ID, every way to read it – the window.lj JavaScript API, browser storage, a hidden form field, the clickId cookie on your server and the lj_click_id URL parameter – and how to combine them so your own booking system, form or backend sends a click_id with every event.
When you send events yourself – from a custom booking system, an in-house form or your own
backend – the click_id is what ties each event to the exact campaign, ad set and ad. This page
shows where the tracking script keeps the click ID, every way to read it, and how to combine
those ways so you still have the ID when one of them fails.
In short
In the browser, read the click ID when the form is submitted with
window.lj.getClickId(), and fall back to browser storage and the URL.
Add a hidden lj_click_id field to the form as a second path, and read the clickId cookie
on your server as a third.
Save the ID on the booking or contact record and send it with every event – together
with email and phone.
When a visitor lands with lj_click_id in the URL (every tracked link carries it), the tracking
script saves that ID. A visitor without one gets a new click ID from the script. So on any page
where the script has run, the visitor has a click ID – and it's stored in several places at once:
Where
Name
Kept for
Who can read it
URL parameter
lj_click_id
Until the script has saved it – then it's removed from the address bar
JavaScript on that page load
localStorage
clickId
Until the visitor clears site data
JavaScript on the same host
Cookie
clickId
60 days
JavaScript on the same host and your server
sessionStorage
clickId
Until the tab is closed
JavaScript in the same tab
Storage doesn't cross domains – or subdomains
All of these belong to the exact host the script ran on. www.yourbrand.com and
booking.yourbrand.com can't read each other's storage or cookie. Between hosts, the click ID
has to travel in the URL as lj_click_id – see Across domains, subdomains and
iframes.
Read it when the form is submitted, not on page load
After a page loads, the script reports the visit to LeadJourney. The response can contain a
corrected click ID – the script then replaces every stored copy and every filled hidden
field. Reading the ID at the moment of submit always gives you the final value.
window.lj only exists after the script has loaded. If an ad blocker or consent tool stops
the script, or it isn't installed on the page, window.lj is undefined – which is why the
recommended setup falls back to the other ways.
You can read the stored values directly. This still works when the script is blocked on the
current page view, as long as it ran on an earlier visit to the same host:
Add a hidden input to your form – no JavaScript needed. The script fills it with the click ID
automatically:
<input type="hidden" name="lj_click_id" />
The script recognises the names lj_click_id, lj_clickid, click_id and clickid (also with a
trailing _, and as form_fields[lj_click_id] for Elementor), and inputs whose aria-label is one
of those names. Fields added after page load – a later step of a multi-step form, a widget that
renders late – are filled as soon as they appear. If your own code resets the value, call
window.lj.mountClickId() to fill it again.
Two limits of the hidden field
Only the first matching field on the page is filled. With several forms on one page, set
the value yourself when the form is submitted (see step 2 below).
The script sets the value directly, without an input event. React, Vue and other
controlled forms don't see it in their state – read the ID with getClickId() at submit
instead.
The browser sends the clickId cookie with every request to the same host the script runs
on. If your booking form submits to that host, your server can read the ID without any
JavaScript:
// with the cookie-parser middlewareconst clickId = req.cookies.clickId ?? null;
$clickId = $_COOKIE['clickId'] ?? null;
click_id = request.cookies.get("clickId")
The cookie is set for the exact host only. A request from www.yourbrand.com to
api.yourbrand.com does not carry it.
On a page without the tracking script, the click ID arrives as ?lj_click_id=… – for example
when a visitor clicks from your website to a booking system on another host. It's only in the URL
of that first page, so read it once on page load and keep it:
// Only on pages WITHOUT the tracking script – run once on page loadconst fromUrl = new URLSearchParams(window.location.search).get("lj_click_id");if (fromUrl) { try { localStorage.setItem("clickId", fromUrl); } catch {}}
Storing it under clickId means the helper below finds it the same way as on pages with the
script. On pages with the script, don't rely on the URL: the script removes lj_click_id from
the address bar as soon as it has saved the ID.
No single way works everywhere – scripts get blocked, forms live on other subdomains, React forms
ignore hidden fields, and bookings often reach your system days before the next event. Layer the
ways so that each one covers the gaps of the others.
Call the helper at submit and send the result to your backend. Keep the hidden lj_click_id field
in the form as well – if your JavaScript fails, the field still carries the ID.
<form id="booking-form" method="post" action="/bookings"> <!-- your fields: name, email, phone, date … --> <input type="hidden" name="lj_click_id" /></form><script> const form = document.getElementById("booking-form"); form.addEventListener("submit", async (event) => { event.preventDefault(); const field = form.elements.namedItem("lj_click_id"); if (!field.value) field.value = (await getClickId()) ?? ""; form.submit(); });</script>
Store the click ID on the record the moment the booking is created – for example in a column
called lj_click_id on the booking and on the contact. Later milestones (the call took place,
the deal was won) happen in your system, often days later and without a browser. They read the
stored ID back instead of looking for it again.
Never hold back an event because the click ID is missing
If no click ID could be found, send the event anyway. LeadJourney also matches on email
and phone, so the booking is still captured and can be attributed.
Because storage belongs to one host, the click ID has to travel in the URL whenever your
booking system runs somewhere else than the page the visitor landed on.
The booking form is on the same host as the rest of your site (e.g. yourbrand.com/booking).
Nothing extra to do – the helper, the hidden field and the cookie all work.
The booking system runs on a subdomain (e.g. booking.yourbrand.com). Turn on Link
decoration under Settings →
Tracking: the script then adds
lj_click_id to every link that points to your root domain or one of its subdomains.
Script installed on the subdomain too: it picks the ID up from the URL and stores it there
– use the helper as usual.
No script on the subdomain: read the ID from the URL with the snippet under From the
URL.
The booking system runs on a different domain (e.g. yourbooking.com). Link decoration leaves
links to other domains untouched, so add the click ID yourself:
getClickId().then((clickId) => { if (!clickId) return; document.querySelectorAll('a[href^="https://yourbooking.com"]').forEach((link) => { const url = new URL(link.href); url.searchParams.set("lj_click_id", clickId); link.href = url.toString(); });});
On the booking domain, read lj_click_id from the URL (see From the URL) – or
install the tracking script there with its own tracking domain, and it does that for you.
The booking system is embedded as an iframe. The embedded page has its own storage, separate
from your site, so pass the click ID in the iframe's src:
<iframe id="booking" title="Book a call" width="100%" height="700"></iframe><script> getClickId().then((clickId) => { const src = new URL("https://yourbooking.com/embed"); if (clickId) src.searchParams.set("lj_click_id", clickId); document.getElementById("booking").src = src.toString(); });</script>
Inside the booking page, read lj_click_id from its own URL and send it along with the booking.
Using Calendly, Cal.com, HubSpot or Typeform?
For supported tools, the script already passes the click ID into their embeds. This page is
for your own booking system or form – for the others, see
Apps.
Add <input type="hidden" name="lj_tracking_status" /> to your form – the script fills it with
the status, so you can see for every booking whether tracking worked.