Install with a script tag
Last updated Oct 29, 2026
Paste one version-pinned script tag, grant consent, and confirm the declared path with the events TraceLog receives.
The script tag works on any site you can add HTML to. Copy it from Installation and keys in your project and add it to every page of your site, before your own scripts. It comes filled in with your browser key and a fixed version.
Paste the snippet
The version and key below are placeholders; Installation and keys gives you the real ones.
<script>
(function(n){if(!n.TraceLog){var a=[],e={consent:{}},r=function(t,c){return function(){var o=arguments,i=function(u){(c?u[t][c]:u[t]).apply(u,o)};if(n.TraceLog!==e)return i(n.TraceLog);a.length<100&&a.push(i)}};e.init=r("init"),e.step=r("step"),e.conversion=r("conversion"),e.consent.grant=r("consent","grant"),e.consent.deny=r("consent","deny"),e.consent.state=function(){if(n.TraceLog!==e)return n.TraceLog.consent.state();try{if(n.localStorage.getItem("__tl.c")==="denied")return"denied"}catch(t){}return"unknown"},Object.defineProperty(n,"TraceLog",{configurable:!0,get:function(){return e},set:function(t){delete n.TraceLog,n.TraceLog=t;for(var c=a.splice(0),o=0;o<c.length;o++)try{c[o](t)}catch(i){}}})}})(window);
TraceLog.init({ key: "tl_pk_xxxxxxxxxxxxxxxxxxxxxxxxxx", endpoint: "https://api.tracelog.io/v1/events" });
// Keep capture behind consent; call TraceLog.consent.grant() only after consent is granted.
</script>
<script defer src="https://cdn.tracelog.io/v/2.4.0/tracelog.js"></script>The version is fixed, so you choose when to update it. Your page never waits for TraceLog to load: what it calls before then is held, in order, and handed to TraceLog once it loads.
Grant consent
Before a consent decision the capture code creates no identifier, writes
nothing to storage and sends no request. A denial is remembered in one key and
nothing else; a grant is not remembered. Nothing is captured until you call
grant:
TraceLog.consent.grant();Call it when the visitor accepts, and again on every page load while their consent stands: wire it to the callback your consent platform runs whenever consent is present, not only to the click on accept. If your market lets you run first-party measurement without a banner, call it on every load. TraceLog itself sets no tracking cookie either way, and no cookie of any kind: the session identifier lives in the browser's own local storage.
Send the declared path
Your conversion path names the conversion, the stable identifier that must accompany it, and the steps expected before it. Send each step where it happens, and the conversion where it completes:
TraceLog.step("view_item", undefined, {
items: [{ id: product.sku, name: product.name, quantity: 1 }],
});
TraceLog.step("begin_checkout");
TraceLog.conversion("purchase", {
identifier: order.number,
value: order.total,
currency: "EUR",
items: order.lines.map((line) => ({
id: line.sku,
name: line.name,
quantity: line.quantity,
price: line.unitPrice,
})),
});Use the identifier your own system already has: an order number, a lead id, a booking reference. It lets TraceLog count each conversion once. A conversion that arrives without it blocks verification.
Items are optional: up to 100, each an ID, a name, an optional category, a
quantity and an optional unit price. A list TraceLog cannot read is left out and
the conversion still counts. A booking may send scheduledFor, the day it is
for as YYYY-MM-DD, a subscription or a donation recurring, and any
conversion first (below).
Every option is in the SDK reference.
With Google Tag Manager
If Google Tag Manager loads the tags on your site, TraceLog can go in its container instead of in your pages: one way or the other, never both. Every tag here is a Custom HTML tag.
The TraceLog tag loads TraceLog once per page and tells Tag Manager when it is ready. Installation and keys gives you this tag with your browser key filled in; the key below is a placeholder. Fire it on Initialization - All Pages:
<script>
(function () {
var gtm = window.google_tag_manager[{{Container ID}}];
var ready = function () {
gtm.onHtmlSuccess({{HTML ID}});
};
if (window.TraceLog) return ready();
var script = document.querySelector("script[data-tracelog]");
if (!script) {
script = document.createElement("script");
script.src = "https://cdn.tracelog.io/v/2.4.0/tracelog.js";
script.setAttribute("data-tracelog", "");
script.addEventListener("load", function () {
TraceLog.init({
key: "tl_pk_xxxxxxxxxxxxxxxxxxxxxxxxxx",
endpoint: "https://api.tracelog.io/v1/events",
});
});
document.head.appendChild(script);
}
script.addEventListener("load", ready);
})();
</script>Grant consent from a tag fired on every page where the visitor has consented — the trigger your consent platform provides for that, not one that fires only on the click — or on Initialization - All Pages where no banner is needed. Where your banner offers no such trigger and pushes no event to the data layer, have the code it runs on consent push one — on the accept and on every later page load while the consent stands — and fire the tag on that event. Tag Manager's own consent settings do not stand in for the trigger: a tag they hold back is not fired later on that page.
<script>
TraceLog.consent.grant();
</script>Then add one tag for each call in the code of your path on Installation and keys, fired by a Custom Event trigger on the event your site pushes to the data layer at that moment — the one your analytics tag for that moment already fires on. The names in your path are your own: they need not match the data layer's.
<script>
TraceLog.step("add_to_cart");
</script>In the consent tag and in each of these, open Advanced Settings, then Tag Sequencing, and choose the TraceLog tag under Fire a setup tag before this tag fires. No call then runs before TraceLog has loaded.
The conversion takes its identifier, value, currency and items from four Data
Layer Variables: the ones your purchase tag already reads. On a data layer
shaped for Google Analytics 4 — whose ecommerce events, view_item,
add_to_cart, begin_checkout and purchase, are the purchase template's
names — they are ecommerce.transaction_id, ecommerce.value,
ecommerce.currency and ecommerce.items, named here Order number, Order
value, Order currency and Order items. The identifier goes in as text, which
String makes sure of:
<script>
TraceLog.conversion("purchase", {
identifier: String({{Order number}}),
value: {{Order value}},
currency: {{Order currency}},
items: ({{Order items}} || []).map(function (item) {
return {
id: String(item.item_id || item.item_name),
name: String(item.item_name || item.item_id),
category: item.item_category,
quantity: item.quantity || 1,
price: item.price,
};
}),
});
</script>A data layer names an item's fields its own way, so the tag maps them.
Send items on the steps that have them too. The sessions that saw an item are
those in which a step carried it: with items on the purchase alone, you know
what sold but not what was seen. On a Google Analytics 4 data layer,
view_item and add_to_cart bring their list in the same ecommerce.items,
so the tag for each of those steps reads the same Order items variable:
<script>
TraceLog.step("add_to_cart", undefined, {
items: ({{Order items}} || []).map(function (item) {
return {
id: String(item.item_id || item.item_name),
name: String(item.item_name || item.item_id),
category: item.item_category,
quantity: item.quantity || 1,
price: item.price,
};
}),
});
</script>Say whether an order is the customer's first
A conversion may say whether it is the person's first of its kind: first: true
when your records hold no earlier one by the same person, false when they do.
From the Pro plan, Analytics then shows the First-time share: among the
orders that said either way, the share that were the customer's first, also by
channel or campaign. It covers purchases, bookings and donations. The
WooCommerce plugin and the PrestaShop module send it on their own from version
0.5.0; TraceLog's Shopify app does not send it.
Your site decides it from its own records, such as the customer's earlier paid
orders, and sends only true or false: no identifier or detail of the person
reaches TraceLog. When you do not know, as with some guest checkouts, leave it
out.
TraceLog.conversion("purchase", {
identifier: order.number,
value: order.total,
currency: "EUR",
first: customer.earlierPaidOrders === 0,
});Only true and false count: the text "true", a 1 or an empty value is
left out without an error, and the conversion still counts.
In Google Tag Manager, read it with one more Data Layer Variable (here,
First order) from the value your site pushes with purchase. If your site
already pushes new_customer for Google Ads, read that one: it means the same,
as long as your site works it out from all its past orders. If your data layer
has neither, whoever builds your site adds it to the purchase event. Then add
this line to the purchase tag, after currency; it also turns the text "true"
or "false" into the true or false TraceLog reads:
first: {{First order}} === true || {{First order}} === "true" ? true
: {{First order}} === false || {{First order}} === "false" ? false
: undefined,Several domains and staging
The browser key is accepted only from your site's addresses. If the same
project runs on more than one domain, add each one under Settings → Installation and keys → Your site's addresses, whole, as in
https://shop.example. Events from any other address are refused.
A staging copy of your site can send to the same project. Add it in two
places: its whole address under Settings → Installation and keys →
Your site's addresses, as in
https://staging.shop.example, so the key is accepted from it, and its
hostname under Settings → Staging and internal traffic → Staging hostnames, as in staging.shop.example, so its events are not
taken for real ones. Both lists match the exact address and take no wildcards:
each environment is added on its own. Its events are staging traffic:
they count toward neither the figures nor the session ceiling, and Path
shows, under each step, when its last staging event arrived.
Verify it
With Google Tag Manager, publish the container first: a tag in an unpublished workspace reaches no visitor.
Open Installation and keys in the project and start the check from Open my site. The tab that button opens is the one TraceLog marks as verification traffic. Accept your consent banner in it first: from then on the tab stays in the check across your pages and through a payment that leaves your site and comes back. A conversion you complete in any other tab counts as real and reaches your figures.
Walk the path through to the conversion in that tab, and leave Installation and keys open beside it. The checklist under Your conversion path ticks as TraceLog receives each declared event. Verification traffic is marked, counts towards no limit, and stays visible on the Path page afterwards.
If you close that tab, open your site from Open my site again and walk the path once more.
If it does not tick
| Condition | Effect on verification | What to do |
|---|---|---|
| sdk_absent | Reports only | Check that the TraceLog snippet is installed and that your consent setup is not preventing it from loading. |
| consent_blocked | Reports only | Check that your consent platform allows TraceLog for real visitors; during the check, grant consent first. |
| config_invalid | Reports only | Replace the installed configuration with the current pre-filled snippet for this project. |
| delivery_failing | Reports only | TraceLog is rejecting events from this site. |
| path_missing | Reports only | TraceLog receives visits from this site, but no step or conversion has arrived yet. Call TraceLog.step where each step happens and TraceLog.conversion where the conversion completes. |
| conversion_missing | Reports only | Trigger the declared conversion and check that its event is sent after the preceding steps. |
| conversion_unidentified | Blocks verification | A conversion arrived without its stable identifier. Send the declared stable identifier with every conversion event. |
| conversion_duplicated | Blocks verification | Give each conversion its own identifier, and always send it with the same value and currency. |
| conversion_value_conflict | Warns | A conversion's identifier, such as its order number, arrived again with a different value. Check that each identifier belongs to one conversion and is always sent with the same value. |
| conversion_unanchored | Warns | Send at least one declared preceding step in the same session before the conversion. |
| conversion_resent | Warns | Some conversions arrive again under an identifier that had already arrived. TraceLog counts each conversion once. Send it only when it happens: a page the buyer comes back to later, such as the cart or the return from a payment provider, should not send it again. |
| value_implausible | Warns | Check that the value and currency match the sale; a zero value or another currency can be correct. |
| coverage_gap | Warns | Visitors on one device type (desktop, mobile or tablet) reach the last step often enough that some should have converted, and none has. Complete the path on each device type and check that the conversion arrives. |
| server_evidence_missing | Warns | Check that your store's server confirmation of the order is reaching TraceLog. |
| payment_unmatched | Warns | Give Stripe the same identifier your page sends, one for each order. |
| vendor_fault | Reports only | Events stopped arriving here and, at the same time, on other unrelated sites, so TraceLog is looking into it; you don't need to change anything on your site. |
With no events arriving, the first three rows look the same. In the tab opened from Open my site, the capture code reports whether it is loaded, its consent state and whether its configuration is valid. That lets Installation and keys tell the rows apart.