Skip to content
Start free
Menu
Install with a script tag

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.

The snippet

<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.

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:

JavaScript

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:

JavaScript

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:

HTML

<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.

HTML

<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.

HTML

<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:

HTML

<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:

HTML

<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.

JavaScript

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:

JavaScript

    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

ConditionEffect on verificationWhat to do
sdk_absentReports onlyCheck that the TraceLog snippet is installed and that your consent setup is not preventing it from loading.
consent_blockedReports onlyCheck that your consent platform allows TraceLog for real visitors; during the check, grant consent first.
config_invalidReports onlyReplace the installed configuration with the current pre-filled snippet for this project.
delivery_failingReports onlyTraceLog is rejecting events from this site.
path_missingReports onlyTraceLog 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_missingReports onlyTrigger the declared conversion and check that its event is sent after the preceding steps.
conversion_unidentifiedBlocks verificationA conversion arrived without its stable identifier. Send the declared stable identifier with every conversion event.
conversion_duplicatedBlocks verificationGive each conversion its own identifier, and always send it with the same value and currency.
conversion_value_conflictWarnsA 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_unanchoredWarnsSend at least one declared preceding step in the same session before the conversion.
conversion_resentWarnsSome 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_implausibleWarnsCheck that the value and currency match the sale; a zero value or another currency can be correct.
coverage_gapWarnsVisitors 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_missingWarnsCheck that your store's server confirmation of the order is reaching TraceLog.
payment_unmatchedWarnsGive Stripe the same identifier your page sends, one for each order.
vendor_faultReports onlyEvents 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.