Frontend IntegrationWeb ObjectIntegration guide

Integration guide

How to deliver @usesophi/sophi-web-object to your storefront — choose between GTM, npm/bundler, or CDN script tag depending on your stack.

Choose how you deliver @usesophi/sophi-web-object. For Google Tag Manager, the detailed walkthrough is on a dedicated page so it is easy to find: GTM integration.

Chat widget

To load the Sophi assistant widget (SophiWidget, @usesophi/sophi-web-sdk) from GTM, use SDK GTM integration, not this package.

Choose an integration method

MethodBest whenUpgrade story
GTMMarketing owns tag publishing; consent-gated load; you want one place to bump the script URLPin SDK version in a GTM Constant variable; change it + publish container
Script tagFull control in your HTML templates; no GTMChange the src URL (or your CMS snippet) and deploy
npm / ESMBundled app; type-aware codebase; tree-shaking not required for this global SDKBump dependency in package.json and release your app

All methods expose window.sophi_object and window.SophiTracker (see SophiTracker API).

Script tag

The inline snippet must come before the async SDK script tag and must contain both the config object and the official pre-load stub:

<script>
  window.sophi_object = {
    config: {
      apiKey: 'YOUR_API_KEY'
    },
    page: {
      type: 'Home',
      url: window.location.href
    }
  };

  window.SophiTracker = window.SophiTracker || (function () {
    var _q = [];
    var _fwd = null;
    function push() {
      if (_fwd) { return _fwd.apply(null, arguments); }
      _q.push(Array.prototype.slice.call(arguments));
    }
    push._sophiQ = _q;
    push._sophiSetFwd = function (fn) { _fwd = fn; };
    return { push: push };
  }());
</script>

<script
  src="https://cdn.jsdelivr.net/npm/@usesophi/sophi-web-object@1.3.x/dist/sophi.min.js"
  async
></script>
Version pinning

The URL above uses @1.3.x, which automatically selects the latest compatible patch within the 1.3 minor line — you get bug fixes without risking a breaking minor upgrade. Never use @latest in production. Check Installation for the full CDN table.

Why the stub is required

The SDK script loads asynchronously. Between the moment the inline snippet runs and the moment the SDK bundle executes, window.SophiTracker does not exist yet. Without the stub, any push() call in that window is silently lost.

The stub:

  1. Creates window.SophiTracker immediately if it does not already exist.
  2. Buffers every push() call into an internal queue (_sophiQ).
  3. Installs a forwarding hook (_sophiSetFwd) that the SDK uses on startup to replay the queue.

The SDK adopts the stub queue and drains all buffered calls in FIFO order after session initialization and initial preload data collection complete.

Call lifecycle

Inline snippet runs
→ window.sophi_object set (config + optional page/product/… data)
→ window.SophiTracker stub installed

push() calls before SDK bundle loads
→ buffered in stub._sophiQ

SDK bundle executes
→ Stub queue migrated to internal pending queue
→ window.SophiTracker replaced with real tracker
→ push() calls between bundle execution and bootstrap → buffered

DOMContentLoaded / bootstrap
→ Session established
→ sophi_object preload data collected (collectAll)
→ Buffered push() calls drained in FIFO order
→ Tracker enters READY state

Later push() calls → processed immediately

Initial-page events

Declare initial page state through window.sophi_object:

window.sophi_object = {
  config: { apiKey: 'YOUR_API_KEY' },
  page: {
    type: 'Product',
    url: 'https://store.com/products/blue-dress',
    title: 'Blue Dress | Store'
  },
  product: {
    id: '1627421',
    // …
  }
};

The SDK processes this snapshot at bootstrap. page produces a page_view event; product produces a product_view event. Both are guaranteed to fire before any buffered push() calls are drained.

Later SPA events

After the initial page load, use SophiTracker.push() for route changes and interaction events:

SophiTracker.push({
  page: { type: 'Category', url: 'https://store.com/dresses' },
  listing: { /* … */ }
});

The stub and SDK automatically buffer calls made before readiness. You do not need to check window.SophiTracker before calling push().

Important warnings

Do not use sophi_object as an event queue

window.sophi_object is a config and state object, not an event queue.

  • Do not assign it as an array: window.sophi_object = window.sophi_object || []
  • Assigning the same property twice before bootstrap keeps only the latest value — earlier values are silently overwritten.
  • Do not replace or reassign window.sophi_object after the SDK Proxy has been installed.
Do not duplicate events

If you set page through sophi_object preload and also call SophiTracker.push({ page: … }) for the same event, two page_view events will be sent. Use only one mechanism per event.

SPA back/forward navigation

Browser back/forward route changes must still trigger an explicit SophiTracker.push() call from your router. The SDK does not automatically detect history changes.

Consent-denied events are not replayed

If config.storageConsent: false is set at startup, calls buffered in the pre-load queue are discarded when the SDK initialises — not held for later replay. Events begin flowing only after SophiTracker.setConsent(true) is called, at which point the SDK re-collects the current sophi_object snapshot.

npm / ESM

npm install @usesophi/sophi-web-object

ES module import is hoisted, so assign window.sophi_object first, then load the SDK with dynamic import (or a small separate entry file that runs before your main bundle):

window.sophi_object = {
  config: { apiKey: 'YOUR_API_KEY' },
};

await import('@usesophi/sophi-web-object/dist/sophi.esm.js');
// After bootstrap: window.SophiTracker is ready

Alternatively, set window.sophi_object in an inline <script> before your bundled app, or use the script tag / GTM approaches.

Best practices

  • Pin versions in production — Use an exact semver in URLs or package.json, not @latest, so releases do not change behaviour unexpectedly.
  • Consent — Load the tracker and populate PII only when your policy allows (see Configuration and Types for UserObject).
  • SPAs — After route or context changes, update via SophiTracker.push() or assignments; see SophiTracker API and Integration patterns.
  • Config and apiKey — The SDK strips apiKey from the public object after startup; avoid rewriting it on every render.

Events cheat sheet

When you assign to watched keys without overriding event_type, defaults are:

KeyDefault event_type
pagepage_view
useridentify
productproduct_view
basketbasket_view
listinglisting_view
transactionpurchase

For add_to_cart, remove_from_cart, and checkout_started, set event_type explicitly (usually via SophiTracker.push). Details: Event types.

Next steps

On this page