The track Function

Last updated: June 18, 2026

The Noibu JavaScript SDK includes a track() function for sending key ecommerce events (e.g. add to cart, checkout, purchase) to Noibu. Event data is captured in session replays, giving your team additional context into shopper behaviour during issue investigation.

Ecommerce event collection is enabled automatically on the following deployments:

  • Shopify

  • BigCommerce

  • SFCC SFRA

  • Magento 2 (Adobe Commerce) — when the storefront is running the Adobe Commerce Storefront Events SDK. Stores using Adobe's Data Connection module already have the SDK deployed automatically.

    • If your Magento 2 storefront isn't running the Adobe Commerce Storefront Events SDK, you have two options:

      • Install the SDK on your storefront — see Adobe's Install the Storefront Events SDK guide. Once the SDK is present, collection works automatically.

      • Use the track() function below to implement ecommerce event collection manually.

For all other platforms, use the track() function to implement it manually.


Basic structure

window.NOIBUJS.track(eventType, eventData);

Because the Noibu JS SDK is attached to the global window as window.NOIBUJS, make sure it’s available before calling track() (or wait for the noibuSDKReady event).


Parameters

eventType: string (required)

One of the following supported event types:

  • product_added_to_cart — logged when a customer adds a product to their cart

  • product_removed_from_cart — logged when a customer removes a product from their cart

  • checkout_started — logged when a customer begins the checkout process

  • payment_info_submitted — logged when a customer submits their payment information

  • checkout_completed — logged when a customer completes a purchase

  • search_submitted — logged when a customer performs a search on the storefront

  • collection_viewed — logged when a customer visits a product collection index page

  • product_viewed — logged when a customer visits a product details page

  • cart_viewed — logged when a customer visits the cart page

These event names match Shopify’s standard Web Pixels event names. (Shopify)

eventData: object (required)

Specified per eventType, see docs below.


Return value

track() returns:

{ success: boolean, errors: string[] }
  • success: true when the event is accepted for tracking; otherwise false

  • errors: a list of validation errors (empty when success is true)


Examples

Wait for SDK readiness (recommended)

async function trackCheckoutStarted() {
  if (!window.NOIBUJS) {
    await new Promise((resolve) => window.addEventListener("noibuSDKReady", resolve));
  }

  const result = window.NOIBUJS.track("checkout_started", {
    // Shopify-standard payload shape; all fields optional
    checkout: {
      token: "example-checkout-token",
      totalPrice: { amount: 49.99, currencyCode: "USD" },
    },
  });

  if (!result.success) console.warn("Noibu track failed:", result.errors);
}

Event Types Overview

ecomm_name

What we extract

product_viewed

Product variant info

collection_viewed

Collection title

search_submitted

Search query

product_added_to_cart

Cart line item + cost

product_removed_from_cart

Cart line item + cost

cart_viewed

Cart total + currency

checkout_started

Full checkout snapshot

checkout_contact_info_submitted

Event count only (no payload fields)

checkout_address_info_submitted

Delivery options

checkout_shipping_info_submitted

Delivery options

payment_info_submitted

Delivery + payment + pricing

checkout_completed

Full checkout snapshot + order info


Fields by Event

product_viewed

Purpose: Tracks which products users view

Extracted field

JSON path in ecomm_data

SKU

productVariant.sku

Product title

productVariant.product.title

Variant title

productVariant.title

Product type

productVariant.product.type

Product vendor

productVariant.product.vendor

Variant ID

productVariant.id

Product ID

productVariant.product.id

(event count)

Minimum required payload:

{
  "productVariant": {
    "sku": "...",
    "id": "...",
    "title": "...",
    "product": {
      "id": "...",
      "title": "...",
      "type": "...",
      "vendor": "..."
    }
  }
}

collection_viewed

Purpose: Tracks which collections/categories users browse.

Extracted field

JSON path in ecomm_data

Collection title

collection.title

(event count)

Minimum required payload:

{
  "collection": {
    "title": "..."
  }
}

search_submitted

Purpose: Tracks site search queries

Extracted field

JSON path in ecomm_data

Search query

searchResult.query

(event count)

Minimum required payload:

{
  "searchResult": {
    "query": "..."
  }
}

product_added_to_cart

Purpose: Tracks products added to cart

Extracted field

JSON path in ecomm_data

SKU

cartLine.merchandise.sku

Product title

cartLine.merchandise.product.title

Variant title

cartLine.merchandise.title

Product type

cartLine.merchandise.product.type

Product vendor

cartLine.merchandise.product.vendor

Variant ID

cartLine.merchandise.id

Product ID

cartLine.merchandise.product.id

Quantity

cartLine.quantity

Line cost

cartLine.cost.totalAmount.amount

Currency code

cartLine.cost.totalAmount.currencyCode

(event count)

Minimum required payload:

{
  "cartLine": {
    "quantity": 1,
    "merchandise": {
      "sku": "...",
      "id": "...",
      "title": "...",
      "product": {
        "id": "...",
        "title": "...",
        "type": "...",
        "vendor": "..."
      }
    },
    "cost": {
      "totalAmount": {
        "amount": "29.99",
        "currencyCode": "USD"
      }
    }
  }
}

product_removed_from_cart

Purpose: Tracks products removed from cart. Same structure as product_added_to_cart.

Extracted field

JSON path in ecomm_data

SKU

cartLine.merchandise.sku

Product title

cartLine.merchandise.product.title

Variant title

cartLine.merchandise.title

Product type

cartLine.merchandise.product.type

Product vendor

cartLine.merchandise.product.vendor

Variant ID

cartLine.merchandise.id

Product ID

cartLine.merchandise.product.id

Quantity

cartLine.quantity

Line cost

cartLine.cost.totalAmount.amount

(event count)

Minimum required payload: Same structure as product_added_to_cart.


cart_viewed

Purpose: Tracks cart page views

Extracted field

JSON path in ecomm_data

Cart total

cart.cost.totalAmount.amount

Cart quantity

cart.totalQuantity

Currency code

cart.cost.totalAmount.currencyCode

(event count)

Minimum required payload:

{
  "cart": {
    "totalQuantity": 3,
    "cost": {
      "totalAmount": {
        "amount": "89.97",
        "currencyCode": "USD"
      }
    }
  }
}

checkout_started

Purpose: Snapshot of cart at checkout initiation

Extracted field

JSON path in ecomm_data

Line items

checkout.lineItems[]

— Line item SKU

checkout.lineItems[].variant.sku

— Line item title

checkout.lineItems[].title

— Variant title

checkout.lineItems[].variant.title

— Product type

checkout.lineItems[].variant.product.type

— Product vendor

checkout.lineItems[].variant.product.vendor

— Variant ID

checkout.lineItems[].variant.id

— Product ID

checkout.lineItems[].variant.product.id

— Quantity

checkout.lineItems[].quantity

Pricing

Subtotal

checkout.subtotalPrice.amount

Shipping

checkout.shippingLine.price.amount

Tax

checkout.totalTax.amount

Total

checkout.totalPrice.amount

Discounts

Discount codes

checkout.discountApplications[].title

Discount types

checkout.discountApplications[].type

Discount amount

checkout.discountsAmount.amount

Has discount

checkout.discountApplications[] (length > 0)

Other

Currency code

checkout.currencyCode

Cart quantity

(sum of lineItems[].quantity)

(event count)

Minimum required payload:

{
  "checkout": {
    "currencyCode": "USD",
    "lineItems": [
      {
        "title": "...",
        "quantity": "1",
        "variant": {
          "sku": "...",
          "id": "...",
          "title": "...",
          "product": { "id": "...", "type": "...", "vendor": "..." }
        }
      }
    ],
    "subtotalPrice": { "amount": "..." },
    "shippingLine": { "price": { "amount": "..." } },
    "totalTax": { "amount": "..." },
    "totalPrice": { "amount": "..." },
    "discountsAmount": { "amount": "..." },
    "discountApplications": [
      { "title": "...", "type": "..." }
    ]
  }
}

checkout_contact_info_submitted

Purpose: Tracks that the user submitted contact info during checkout.

Extracted field

JSON path in ecomm_data

Has discount

checkout.discountApplications[] (length > 0)

(event count)

Minimum required payload: None needed beyond ecomm_name. Discount detection only needs the array to exist (can be empty []).


checkout_address_info_submitted

Purpose: Tracks address submission and delivery options

Extracted field

JSON path in ecomm_data

Delivery option names

checkout.delivery.selectedDeliveryOptions[].title

Delivery option types

checkout.delivery.selectedDeliveryOptions[].type

(event count)

Minimum required payload:

{
  "checkout": {
    "delivery": {
      "selectedDeliveryOptions": [
        { "title": "Standard Shipping", "type": "SHIPPING" }
      ]
    }
  }
}

checkout_shipping_info_submitted

Purpose: Tracks shipping info submission

Extracted field

JSON path in ecomm_data

Delivery option names

checkout.delivery.selectedDeliveryOptions[].title

Delivery option types

checkout.delivery.selectedDeliveryOptions[].type

(event count)

Minimum required payload: Same as checkout_address_info_submitted.


payment_info_submitted

Purpose: Tracks payment info submission

Extracted field

JSON path in ecomm_data

Delivery option names

checkout.delivery.selectedDeliveryOptions[].title

Delivery option types

checkout.delivery.selectedDeliveryOptions[].type

Payment gateways

checkout.transactions[].gateway

Payment method types

checkout.transactions[].paymentMethod.type

Payment method names

checkout.transactions[].paymentMethod.name

Total price

checkout.totalPrice.amount

Cart quantity

(sum of lineItems[].quantity)

Currency code

checkout.currencyCode

Has discount

checkout.discountApplications[] (length > 0)

(event count)

Minimum required payload:

{
  "checkout": {
    "currencyCode": "USD",
    "totalPrice": { "amount": "..." },
    "delivery": {
      "selectedDeliveryOptions": [
        { "title": "...", "type": "..." }
      ]
    },
    "transactions": [
      {
        "gateway": "shopify_payments",
        "paymentMethod": { "type": "CREDIT_CARD", "name": "Visa" }
      }
    ],
    "discountApplications": []
  }
}

checkout_completed

Purpose: Final order snapshot

Extracted field

JSON path in ecomm_data

Line items

checkout.lineItems[]

— SKU, title, variant, type, vendor, IDs, quantity

(same paths)

Pricing

Subtotal

checkout.subtotalPrice.amount

Shipping

checkout.shippingLine.price.amount

Tax

checkout.totalTax.amount

Total

checkout.totalPrice.amount

Discounts

Discount codes

checkout.discountApplications[].title

Discount types

checkout.discountApplications[].type

Discount amount

checkout.discountsAmount.amount

Has discount

checkout.discountApplications[] (length > 0)

Order info

Order ID

checkout.order.id

Customer ID

checkout.order.customer.id

Delivery

Delivery names

checkout.delivery.selectedDeliveryOptions[].title

Delivery types

checkout.delivery.selectedDeliveryOptions[].type

Payment

Payment gateways

checkout.transactions[].gateway

Payment method types

checkout.transactions[].paymentMethod.type

Payment method names

checkout.transactions[].paymentMethod.name

Other

Currency code

checkout.currencyCode

Cart quantity

(sum of lineItems[].quantity)

(event count)

Minimum required payload:

{
  "checkout": {
    "currencyCode": "USD",
    "order": {
      "id": "...",
      "customer": { "id": "..." }
    },
    "lineItems": [
      {
        "title": "...",
        "quantity": "1",
        "variant": {
          "sku": "...",
          "id": "...",
          "title": "...",
          "product": { "id": "...", "type": "...", "vendor": "..." }
        }
      }
    ],
    "subtotalPrice": { "amount": "..." },
    "shippingLine": { "price": { "amount": "..." } },
    "totalTax": { "amount": "..." },
    "totalPrice": { "amount": "..." },
    "discountsAmount": { "amount": "..." },
    "discountApplications": [
      { "title": "...", "type": "..." }
    ],
    "delivery": {
      "selectedDeliveryOptions": [
        { "title": "...", "type": "..." }
      ]
    },
    "transactions": [
      {
        "gateway": "...",
        "paymentMethod": { "type": "...", "name": "..." }
      }
    ]
  }
}

Custom Events

In addition to the built-in ecommerce events, you can track any custom event using the same track() function:

window.NOIBUJS.track(eventName, eventData);

Example:

window.NOIBUJS.track('user_signed_up', {
  plan: 'pro',
  signupSource: 'homepage_banner',
});

Custom events will be available in Explorations alongside your standard ecommerce events.

Requirements

Parameter

Type

eventName

string

eventData

object or null

Passing null as eventData is valid when there is no associated payload.

Return Value

Same as standard events — an object with success (boolean) and errors (string array):

const result = window.NOIBUJS.track('my_event', { foo: 'bar' });

if (!result.success) {
  console.warn('Tracking failed:', result.errors);
}

Validation Errors

Error

Cause

Event name must be a string.

eventName is not a string

Event payload must be an object or null.

Payload is a primitive (string, number, etc.)

Event payload is not JSON-serializable.

Payload contains circular references or non-serializable values

How to ensure all your tracked events in GA4 are captured in Noibu

Noibu can record the same events you already send to Google Analytics 4 (GA4).
For every GA4 event you fire, you add one matching line that forwards the same
event to Noibu. If a developer of reasonable skill set up your GA4 events,
mirroring them into Noibu takes only a few minutes.

TL;DR — Add the small noibuTrack helper below once, then wherever you push
an event to GA4, call noibuTrack('<event_name>', <event_data>) with the same
name and the same data object.


How GA4 events look today

GA4 events reach Google in one of two ways. Find which one your site uses — the
translation is the same in both cases.

A. Google Tag (gtag.js)

gtag('event', 'add_to_cart', {
  currency: 'USD',
  value: 30.03,
  items: [
    {
      item_id: 'SKU_123',
      item_name: 'Stan and Friends Tee',
      price: 9.99,
      quantity: 3,
    },
  ],
});

B. Google Tag Manager (dataLayer)

window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
  event: 'add_to_cart',
  ecommerce: {
    currency: 'USD',
    value: 30.03,
    items: [
      {
        item_id: 'SKU_123',
        item_name: 'Stan and Friends Tee',
        price: 9.99,
        quantity: 3,
      },
    ],
  },
});

In both, an event has a name (add_to_cart) and a data object (the
ecommerce block / the third argument).


How you send events to Noibu

Noibu exposes a track function on the global window.NOIBUJS object:

window.NOIBUJS.track(eventName, eventData);
  • eventName — a string. Use the same name you use in GA4.

  • eventData — any JSON-serializable object (or null). Pass the same data
    you send to GA4.

However, you should not call window.NOIBUJS.track directly.window.NOIBUJS only exists after the Noibu script has finished loading.
Because of script ordering and async loading, your event can easily fire before
that — and the call would throw or be lost. This is exactly the problem GA4 solves
with the dataLayer = dataLayer || [] queue that buffers events until gtag.js
loads.

There are two ways to handle this. The recommended one for mirroring GA4 events is
the noibuTrack snippet (below). The other is to wait for the SDK before calling.

Option A — the noibuTrack snippet (recommended for GA4 events)

noibuTrack(name, data); // fire-and-forget, callable anywhere

For mirroring GA4 events, Option B is usually preferable because:

  • It matches how you already call GA4. gtag('event', ...) and
    dataLayer.push(...) are synchronous, fire-and-forget calls. noibuTrack is the
    same, so you can drop it right next to your existing GA4 calls without turning
    every one of them into an async function.

  • It works in any context — inline handlers, synchronous code paths, and code
    that cannot easily await.

  • It degrades cleanly. If Noibu never loads, buffered events simply stop at the
    cap (below) instead of leaving awaits pending forever.

The trade-off: events buffered before the SDK loads do not return a
{ success, errors } result (the call is fire-and-forget). At GA4-mirroring volume
that is rarely something you check per call — see
Validating your setup for how to verify during
development.

Use noibuTrack everywhere you mirror a GA4 event. The rest of this guide uses it.

Option B — wait for the SDK to be ready

Noibu fires a noibuSDKReady event once the SDK is loaded. You can await it, then
call track directly:

if (!window.NOIBUJS) {
  await new Promise(resolve =>
    window.addEventListener('noibuSDKReady', resolve),
  );
}
const result = window.NOIBUJS.track(name, data);

This works on the SDK available today and gives you the { success, errors }
return value, so it is a good fit for a small number of deliberate events where you
want to confirm the payload validated.

Its drawback is that every call site must be async and wrapped in this guard, and
if the Noibu script never loads (e.g. blocked by an ad blocker) an awaiting call
never resolves.


Step 1 — Add the noibuTrack snippet

Add this snippet once, as early as possible on the page (before any code that
might fire an event):

<script>
  window.noibuTrack = window.noibuTrack || function () {
    var q = (window.noibuTrack.q = window.noibuTrack.q || []);
    if (q.length < 100) q.push(arguments);
  };
</script>

This is all you add to your page. The snippet simply buffers your noibuTrack
calls; once the Noibu script finishes loading it takes over noibuTrack, replays
the buffered events, and forwards every later call straight to Noibu. It works the
same way GA4’s dataLayer queues events before gtag.js is ready.

The buffer is capped at 100 events so it can never grow without bound if the Noibu
script does not load (for example, when blocked by an ad blocker).


Step 2 — Mirror each GA4 event

Wherever you fire a GA4 event, add a matching noibuTrack call with the same
name and the same data
.

The add_to_cart example above becomes:

noibuTrack('add_to_cart', {
  currency: 'USD',
  value: 30.03,
  items: [
    {
      item_id: 'SKU_123',
      item_name: 'Stan and Friends Tee',
      price: 9.99,
      quantity: 3,
    },
  ],
});

The data object is stored verbatim, so you can copy your GA4 payload exactly as-is.

Where to put the call

Place the Noibu call right next to the GA4 call so the two never drift apart.

gtag.js:

function trackEvent(name, data) {
  gtag('event', name, data); // existing GA4 call
  noibuTrack(name, data); // mirror to Noibu
}

trackEvent('add_to_cart', { currency: 'USD', value: 30.03, items: [/* ... */] });

Google Tag Manager: add a Custom HTML tag that fires on the same trigger
as your GA4 event tag, reading the values from the dataLayer:

<script>
  noibuTrack({{Event}}, {
    currency: {{DLV - ecommerce.currency}},
    value: {{DLV - ecommerce.value}},
    items: {{DLV - ecommerce.items}},
  });
</script>

({{Event}} and {{DLV - ...}} are GTM built-in / Data Layer Variables — point
them at the same keys your GA4 tag uses.)


Let Claude or Codex do it for you

If you use an AI coding assistant (Claude Code, Codex, Cursor, etc.), point it at your codebase and paste the prompt below. It will find your existing GA4 events and add the matching Noibu calls for you.

I want to mirror all of our Google Analytics 4 (GA4) events into Noibu so they are captured in both tools. Please update this codebase to do that.

1. Add this snippet once, as early as possible on the page (in the <head>, before any analytics scripts and before any code that might fire an event):

   <script>
     window.noibuTrack = window.noibuTrack || function () {
       var q = (window.noibuTrack.q = window.noibuTrack.q || []);
       if (q.length < 100) q.push(arguments);
     };
   </script>

2. Find every place we send a GA4 event — calls like `gtag('event', <name>, <data>)` and `dataLayer.push({ event: <name>, ... })`.

3. Next to each one, add a matching call that forwards the SAME event name and the SAME data object to Noibu:

       noibuTrack(<name>, <data>);

   - Do NOT change or remove the existing GA4 calls.
   - Pass the data object exactly as we send it to GA4 (it is stored verbatim).
   - Make sure noibuTrack is called for every GA4 event, including custom ones.

4. Show me the full list of events you mirrored so I can confirm none were missed.

Review the changes the assistant proposes before shipping them — especially the list of events in step 4 — to make sure every GA4 event you care about is covered.


Validating your setup

When Noibu is loaded, noibuTrack returns a result object you can inspect during
development:

var result = noibuTrack('view_item', { item_id: 'SKU_123' });
console.log(result); // { success: true, errors: [] }

If success is false, errors explains why (e.g. the payload is not
JSON-serializable). For events that were buffered before Noibu loaded,
noibuTrack returns nothing — they are validated and sent once the SDK is ready.

Once verified, your events will appear in the Noibu console alongside your other
data.


Quick reference

GA4

Noibu

Send an event

gtag('event', name, data)

noibuTrack(name, data)

Queue before load

`dataLayer = dataLayer

Event name

string

string

Event data

object

JSON-serializable object or null