Frontend IntegrationWeb ObjectIntegration patterns

Integration patterns

Common integration patterns for page views, SPA navigation, cart events, purchases, and user login/logout using the Sophi Web Object SDK.

Examples use SophiTracker.push; equivalent window.sophi_object.* = ... assignments produce the same events after the SDK has started.

Static / server-rendered page

The full inline snippet (config object + pre-load stub) must appear before the async SDK tag:

<script>
  window.sophi_object = {
    config: {
      apiKey: 'YOUR_API_KEY',
      userId: 'usr-123'
    },
    page: {
      type: 'Product',
      url: 'https://store.com/products/blue-dress',
      title: 'Blue Dress | Store',
      language: 'tr-TR'
    },
    product: {
      id: '1627421',
      category_ids: ['cat-dresses', 'cat-womens'],
      name: 'Blue Dress',
      url: 'https://store.com/products/blue-dress',
      product_image_url: 'https://cdn.store.com/blue.jpg',
      taxonomy: ['Clothing', 'Dresses', 'Evening'],
      currency: 'TRY',
      unit_price: 1200.0,
      unit_sale_price: 990.0,
      in_stock: 1
    }
  };

  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>

This typically emits page_view (from page) and product_view (from product), collected before any buffered push() calls are drained.

Version range

@1.3.x automatically selects the latest compatible patch within the 1.3 minor line. Never use @latest in production. See Installation for the full CDN table.

SPA route change

SophiTracker.push({
  page: {
    type: 'Category',
    url: 'https://store.com/womens/dresses',
    category_id: 'cat-dresses'
  },
  listing: {
    category_id: 'cat-dresses',
    name: "Women's Dresses",
    taxonomy: ['Clothing', 'Dresses'],
    total_results: 234,
    page_number: 1,
    page_size: 24,
    items: [
      {
        product_id: '1627421',
        position: 1,
        name: 'Blue Dress',
        url: 'https://store.com/products/blue-dress',
        product_image_url: 'https://cdn.store.com/blue.jpg',
        currency: 'TRY',
        unit_price: 1200.0,
        unit_sale_price: 990.0,
        in_stock: 1
      }
    ]
  }
});

push() calls made before the SDK is ready are automatically buffered by the stub and drained after initialisation. You do not need to check window.SophiTracker before calling it.

Add to cart

Set event_type explicitly; include product and basket.

SophiTracker.push({
  event_type: 'add_to_cart',
  page: {
    type: 'Product',
    url: 'https://store.com/products/blue-dress'
  },
  product: {
    id: '1627421',
    category_ids: ['cat-dresses'],
    name: 'Blue Dress',
    url: 'https://store.com/products/blue-dress',
    product_image_url: 'https://cdn.store.com/blue.jpg',
    taxonomy: ['Clothing', 'Dresses'],
    currency: 'TRY',
    unit_price: 1200.0,
    unit_sale_price: 990.0,
    in_stock: 1
  },
  basket: {
    currency: 'TRY',
    total: 990.0,
    line_items: [
      {
        product_id: '1627421',
        quantity: 1,
        currency: 'TRY',
        unit_price: 1200.0,
        unit_sale_price: 990.0,
        line_total: 990.0
      }
    ]
  }
});

For remove_from_cart, use the same shape with an updated basket and event_type: 'remove_from_cart'.

Purchase confirmation

page is purchase context, not page_view

page on a purchase push is envelope context (where the order completed). The Web Object emits one purchase event — it does not also fire page_view.

  • page may be included in the same push, or already sit on window.sophi_object from a prior page update.
  • If page is missing entirely, the SDK skips the purchase and warns in the console.
  • To track the confirmation URL as a page view, call SophiTracker.push({ page }) separately (typically from a layout/route tracker).
SophiTracker.push({
  page: {
    type: 'Confirmation',
    url: 'https://store.com/checkout/confirmation/ORD-99231'
  },
  transaction: {
    order_id: 'ORD-99231',
    currency: 'TRY',
    total: 1989.9,
    subtotal: 1980.0,
    tax: 0.0,
    shipping_cost: 9.9,
    discount_total: 210.0,
    coupon_code: 'SS25',
    payment_method: 'credit_card',
    line_items: [
      {
        product_id: '1627421',
        quantity: 2,
        currency: 'TRY',
        unit_price: 1200.0,
        unit_sale_price: 990.0,
        line_total: 1980.0
      }
    ]
  }
});
// Fires: one purchase (page is envelope context, not a sibling page_view)

A transaction-only push is also valid when window.sophi_object.page is already set (for example by a parent route tracker):

SophiTracker.push({
  transaction: { /* order fields */ }
});
// Fires: one purchase using the current sophi_object.page

User login

SophiTracker.updateUser('usr-8842');

SophiTracker.push({
  user: {
    consent: {
      gdpr_optin: true,
      version: '2026-01-15',
      timestamp: Math.floor(Date.now() / 1000)
    },
    identity: {
      uuid: 'usr-8842',
      email: 'jane@example.com'
    },
    profile: {
      name: 'Jane',
      surname: 'Doe',
      gender: 'F'
    },
    behavior: {
      returning: true,
      has_transacted: true,
      transaction_count: 4
    }
  }
});

User logout

SophiTracker.updateUser(undefined);

Clears session state and establishes a new anonymous session for subsequent events.

Important warnings

sophi_object is not an event queue

window.sophi_object holds named state objects (page, product, basket, …), not a list of events.

  • Do not do: window.sophi_object = window.sophi_object || []
  • Assigning the same key twice before bootstrap keeps only the last value.
  • Do not replace window.sophi_object after the SDK Proxy is installed.
Do not duplicate events

Setting an initial value in sophi_object and also calling SophiTracker.push() for the same event will produce two events. Use only one mechanism per event per page load.

SPA back/forward navigation

Browser history navigation does not automatically trigger Sophi events. Your router must call SophiTracker.push() on every route change, including back/forward.

Consent-denied events are not replayed

When storageConsent: false at startup, pre-load buffered events are discarded on bootstrap — not held for replay when setConsent(true) is later called.

On this page