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 cartproduct_removed_from_cart— logged when a customer removes a product from their cartcheckout_started— logged when a customer begins the checkout processpayment_info_submitted— logged when a customer submits their payment informationcheckout_completed— logged when a customer completes a purchasesearch_submitted— logged when a customer performs a search on the storefrontcollection_viewed— logged when a customer visits a product collection index pageproduct_viewed— logged when a customer visits a product details pagecart_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:truewhen the event is accepted for tracking; otherwisefalseerrors: a list of validation errors (empty whensuccessistrue)
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
| What we extract |
|---|---|
| Product variant info |
| Collection title |
| Search query |
| Cart line item + cost |
| Cart line item + cost |
| Cart total + currency |
| Full checkout snapshot |
| Event count only (no payload fields) |
| Delivery options |
| Delivery options |
| Delivery + payment + pricing |
| Full checkout snapshot + order info |
Fields by Event
product_viewed
Purpose: Tracks which products users view
Extracted field | JSON path in |
|---|---|
SKU |
|
Product title |
|
Variant title |
|
Product type |
|
Product vendor |
|
Variant ID |
|
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 |
|---|---|
Collection title |
|
(event count) | — |
Minimum required payload:
{
"collection": {
"title": "..."
}
}
search_submitted
Purpose: Tracks site search queries
Extracted field | JSON path in |
|---|---|
Search query |
|
(event count) | — |
Minimum required payload:
{
"searchResult": {
"query": "..."
}
}
product_added_to_cart
Purpose: Tracks products added to cart
Extracted field | JSON path in |
|---|---|
SKU |
|
Product title |
|
Variant title |
|
Product type |
|
Product vendor |
|
Variant ID |
|
Product ID |
|
Quantity |
|
Line cost |
|
Currency code |
|
(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 |
|---|---|
SKU |
|
Product title |
|
Variant title |
|
Product type |
|
Product vendor |
|
Variant ID |
|
Product ID |
|
Quantity |
|
Line cost |
|
(event count) | — |
Minimum required payload: Same structure as product_added_to_cart.
cart_viewed
Purpose: Tracks cart page views
Extracted field | JSON path in |
|---|---|
Cart total |
|
Cart quantity |
|
Currency code |
|
(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 |
|---|---|
Line items |
|
— Line item SKU |
|
— Line item title |
|
— Variant title |
|
— Product type |
|
— Product vendor |
|
— Variant ID |
|
— Product ID |
|
— Quantity |
|
Pricing | |
Subtotal |
|
Shipping |
|
Tax |
|
Total |
|
Discounts | |
Discount codes |
|
Discount types |
|
Discount amount |
|
Has discount |
|
Other | |
Currency code |
|
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 |
|---|---|
Has discount |
|
(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 |
|---|---|
Delivery option names |
|
Delivery option types |
|
(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 |
|---|---|
Delivery option names |
|
Delivery option types |
|
(event count) | — |
Minimum required payload: Same as checkout_address_info_submitted.
payment_info_submitted
Purpose: Tracks payment info submission
Extracted field | JSON path in |
|---|---|
Delivery option names |
|
Delivery option types |
|
Payment gateways |
|
Payment method types |
|
Payment method names |
|
Total price |
|
Cart quantity | (sum of lineItems[].quantity) |
Currency code |
|
Has discount |
|
(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 |
|---|---|
Line items |
|
— SKU, title, variant, type, vendor, IDs, quantity | (same paths) |
Pricing | |
Subtotal |
|
Shipping |
|
Tax |
|
Total |
|
Discounts | |
Discount codes |
|
Discount types |
|
Discount amount |
|
Has discount |
|
Order info | |
Order ID |
|
Customer ID |
|
Delivery | |
Delivery names |
|
Delivery types |
|
Payment | |
Payment gateways |
|
Payment method types |
|
Payment method names |
|
Other | |
Currency code |
|
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 |
|---|---|
| string |
| 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 |
|---|---|
|
|
| Payload is a primitive (string, number, etc.) |
| 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 (theecommerce 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 (ornull). 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', ...)anddataLayer.push(...)are synchronous, fire-and-forget calls.noibuTrackis the
same, so you can drop it right next to your existing GA4 calls without turning
every one of them into anasyncfunction.It works in any context — inline handlers, synchronous code paths, and code
that cannot easilyawait.It degrades cleanly. If Noibu never loads, buffered events simply stop at the
cap (below) instead of leavingawaits 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 |
|
|
Queue before load | `dataLayer = dataLayer | |
Event name | string | string |
Event data | object | JSON-serializable object or null |