Enable product view signals in the Connect web library

The product view signal captures user activity on product pages, enabling your marketing team to understand which products and product categories receive most attention. This signal is crucial for segmentation and allows you to tie product interest to identified visitors.

What constitutes a product page varies by business:

  • Retail: Product detail page or quick view
  • Travel: Itinerary, room or booking option view
  • Banking: Specific account opening page
  • Insurance: Specific policy application page

Availability: Premium and Ultimate


Implementation considerations

Relationship to product configuration signal

The product view signal works in conjunction with the product configuration signal:

  • Product view - Collects data about the product itself
  • Product configuration - Tracks user interactions on the product page (selecting size, checking reviews, etc.)

Triggering approach

Fire the product view signal each time a product details page or product details modal is loaded. On sites without individual product pages where multiple products are displayed together (such as a hotel room shortlist), send one product view signal for each product shown to the user.

Contact mapping

Any signal can be mapped to a contact. Use the audience object to provide a customer ID (contact key) or an addressable attribute (email or phone number). You can provide several identification records in the same signal — for example, a contact key and an email address. For details on how Connect processes identification records, see How behavior signals are processed in Connect.

Several sources of identification are available:

  • User identification stored in browser cookies
  • Temporary session-based storage
  • Persistent client-side storage
  • Data layer (for example, Google Tag Manager dataLayer)
  • URL parameters passed during navigation
  • Login API monitoring (capture identifier during successful authentication)

If none of these options are available, work with your development team to make the identifier accessible to the Connect library.

🚧

Important

We recommend attaching identifiers to as many signals as possible. However, if an identifier is missing, the system falls back on other signals from the same user session in order to identify the website visitor.

Data layer

If your website has a data layer, it will speed up configuration and make the payload data more reliable. You can scrape product-related values from the DOM or from the data layer.


Configuration

Method

TLT.logSignal(signalObject)

Sends the signal to the Acoustic Connect endpoint as a JSON object. The Connect library must be initialized before calling this method.

Signal structure

Top-level fields

  • audience: Array of objects - Create an object for contact mapping and any number of additional objects to update contact attributes.
  • category: String - Valid value - Behavior.
  • consent: Object - Consent preferences to update for the contact. At least one of the following channel fields is required: email, sms or whatsapp.
  • description: String - Description of the signal
  • effect: String - Describes the effect of the signal on engagement. Valid values: negative, positive. Use positive for all product views.
  • name: String - Name to differentiate this signal from others (for example, "Product view from website"). Max length - 256 chars.
  • signalType (required): String - Valid value - productView.

Audience object

Create an object with one or more identifiers for contact mapping (recommended) and any number of additional objects for other contact attributes you want to update.

  • name (required): String - Name of contact attribute. To get the full list of available attributes, use the Contact attributes query.
  • value: The new value of the contact attribute. Its format depends on the attribute type (text, number, Boolean or date). If you send an empty or null value, it will be ignored and the contact attribute won't be updated.

📘

Note

  • For date attributes, use the following format: yyyy-MM-dd'T'HH:mm:ssXXX.
  • For phone number attributes, use the ISO E.164 format: +[country code][area code][phone number] — no spaces, dashes or special characters. If the + symbol is omitted, Connect adds it automatically.
  • Consent object

Each channel accepts multiple entries to set different consent statuses per consent group in a single call.

  • enableOverrideExistingOptOut (required): Boolean - When true, current opt-out statuses will be overridden by new statuses. When false, contacts who have unsubscribed from communications won't have their consent status updated.
  • email: Array of objects - Consent entries for the email channel. The objects support the following fields:
    • status (required): Enum - Consent status for the email channel. Valid values: OPT_IN, OPT_OUT, OPT_IN_UNVERIFIED.
    • consentGroupIds: Array of strings - The IDs of consent groups a new status applies to. If you skip this field or send an empty array, the new consent status will be applied to all consent groups.
  • sms: Array of objects - Consent entries for the SMS channel. The objects support the following fields:
    • status (required): Enum - Consent status for the SMS channel. Valid values: OPT_IN, OPT_OUT, OPT_IN_UNVERIFIED.
    • consentGroupIds: Array of strings - The IDs of consent groups a new status applies to. If you skip this field or send an empty array, the new consent status will be applied to all consent groups.
  • whatsapp: Array of objects - Consent entries for the WhatsApp channel. The objects support the following fields:
    • status (required): Enum - Consent status for the WhatsApp channel. Valid values: OPT_IN, OPT_OUT, OPT_IN_UNVERIFIED.
    • consentGroupIds: Array of strings - The IDs of consent groups a new status applies to. If you skip this field or send an empty array, the new consent status will be applied to all consent groups.

📘

Note

To get the IDs of consent groups, use the dataSets query.

Signal content

  • availability: String - Indicates if the product is available for purchase (for example, "In Stock", "Out of Stock", "Back Order").
  • brandDescription: String - Description of the brand
  • brandName: String - Brand name of the product
  • categories: String - Product categories
  • currency: String - ISO 4217 currency code (for example, "USD", "EUR", "GBP"). If there is only one currency on your website, provide a static value. If there is a selection, use dynamic values. If a product view signal comes without a value, the system will fall back on the default currency set for your Connect subscription.
  • dateAdded: Date - The date when the product was added to the product catalog. Format: yyyy-MM-dd HH:mm:ss.SSS.
  • discount: Number - Discount amount or difference between original and current price
  • imageUrls: Array of strings - URLs of product images
  • inventoryQuantity: Number - The number of units available
  • model: String - Model number or description
  • msrp: Number - Suggested retail price or original price. Must be a decimal or float.
  • productCategory: String - Product category from catalog
  • productCategoryId: String - Product category identifier
  • productDescription: String - Description of the product
  • productId (required): String - Unique product identifier (may match SKU)
  • productName (required): String - Name of the product
  • productRating: Number - Rating of the product
  • productStatus: String - Current lifecycle state (for example, "Active", "Discontinued", "Upcoming")
  • productUrls: Array of strings - URLs of product pages
  • promotionId: String - ID from marketing campaigns (hero images, CTAs) that led to this view
  • shoppingCartUrl: String - URL of the shopping cart
  • signalCustomAttributes: Array of objects - Additional custom attributes (each object needs name and value string fields)
  • sku: String - SKU (if different from product ID)
  • tags: Array of strings - Tags assigned to the product
  • unitPrice: Number - Unit price of the product
  • virtualCategory: String - Category based on navigation path (for example, "New arrivals", "Sale")

Notes:

  • If any required field is missing or invalid, the entire signal will be discarded.
  • Optional fields enhance the signal but won't prevent processing if omitted or invalid.

Basic example

// Initialize the Connect library
if (window.TLT) {
    TLT.initPremium({
        appKey: "APP_KEY",
        postUrl: "COLLECTOR_URL",
        callback: function() {
            const signal = {
                // Required fields
                signalType: "productView",
                productId: "SKU-12345",
                productName: "Wireless Headphones",

                // Optional fields
                productDescription: "Premium noise-cancelling headphones",
                productCategory: "Electronics",
                unitPrice: 299.99,
                discount: 30.00,
                currency: "USD",
                effect: "positive",
                category: "Behavior",
                name: "productView from website",
                availability: "In Stock",
                inventoryQuantity: 42,
                productUrls: ["https://example.com/products/headphones"],
                imageUrls: ["https://example.com/images/headphones.jpg"],
                shoppingCartUrl: "https://example.com/cart",

                // Contact mapping
                audience: [
                    {
                        name: "Customer ID",
                        value: "AAUN-1417508"
                    }
                ]
            };

            // Send the signal
            TLT.logSignal(signal);
        }
    });
}

Complete implementation example 1 - Product details page

This example captures product view data from a standard product detail page. The audience identifier is extracted from a URL parameter.

// Initialize the Connect library
if (window.TLT) {
    TLT.initPremium({
        appKey: "APP_KEY",
        postUrl: "COLLECTOR_URL",
        callback: function() {
            // Check if we are on a product details page
            const productTile = document.querySelector(".product-tile");

            if (productTile && document.querySelector(".product_description")) {
                const regularPrice = Number(productTile.querySelector(".price-default")?.innerText || 0);
                const discountedPrice = Number(productTile.querySelector(".price-sale")?.innerText || 0);

                const signal = {
                    signalType: "productView",
                    category: "Behavior",
                    name: "productView generated by website",
                    effect: "positive",

                    // Required fields
                    productId: productTile.getAttribute("data-sku") || "",
                    productName: productTile.getAttribute("data-name") || "",

                    // Pricing
                    unitPrice: discountedPrice || regularPrice,
                    discount: discountedPrice ? Math.round((regularPrice - discountedPrice) * 100) / 100 : 0,
                    currency: "USD",

                    // Product details
                    productDescription: document.querySelector(".product_description")?.innerText || "",
                    productCategory: document.getElementById("productCategory")?.innerText || "",
                    sku: productTile.getAttribute("data-sku") || "",

                    // URLs
                    productUrls: productTile.getAttribute("data-url") ? [productTile.getAttribute("data-url")] : [],
                    imageUrls: productTile.getAttribute("data-image-url") ? [productTile.getAttribute("data-image-url")] : [],
                    shoppingCartUrl: "https://www.sample.com/cart"
                };

                // Add contact mapping from URL parameter
                const urlParams = new URLSearchParams(window.location.search);
                const customerId = urlParams.get("customerId");
                if (customerId) {
                    signal.audience = [
                        {
                            name: "Customer ID",
                            value: customerId
                        }
                    ];
                }

                // Optional: Log signal for debugging
                console.log("productView signal:", JSON.stringify(signal, null, 2));

                // Send signal to Connect
                TLT.logSignal(signal);
            }
        }
    });
}

Complete implementation example 2 - Multiple products (hotel rooms)

This example sends separate signals for each product in a list. The audience identifier is retrieved from session storage, where it was stored during login.

// Capture the email address from the login API response and store it
function monitorLoginApi(email) {
    if (email) {
        sessionStorage.setItem("email", email);
    }
}

// Initialize the Connect library
if (window.TLT) {
    TLT.initPremium({
        appKey: "APP_KEY",
        postUrl: "COLLECTOR_URL",
        callback: function() {
            const href = window.location.href;

            // Check if we're on a room selection page with multiple options
            if (href.includes("/reserve-a-room/") && document.querySelector("a.choose-room")) {
                const roomCards = document.querySelectorAll("div.card");

                // Add contact mapping from session storage
                const email = sessionStorage.getItem("email");

                // Send a signal for each room displayed
                roomCards.forEach((card) => {
                    const description = card.querySelector("#desc")?.innerText || "";
                    const roomId = card.getAttribute("room-id") || "";

                    const signal = {
                        signalType: "productView",
                        category: "Behavior",
                        name: "productView generated by website",
                        effect: "positive",

                        // Required fields
                        productId: roomId,
                        productName: roomId,

                        // Pricing
                        unitPrice: Number(card.querySelector(".total-price")?.innerText || 0),
                        currency: "USD",

                        // Product details
                        productDescription: description,
                        productCategory: description.includes("your own kitchen") ? "Self catering" : "Standard room",

                        // URLs
                        shoppingCartUrl: "https://www.sample.com/reservations",
                        productUrls: [],
                        imageUrls: []
                    };

                    if (email) {
                        signal.audience = [
                            {
                                name: "Email",
                                value: email
                            }
                        ];
                    }

                    // Optional: Log signal for debugging
                    console.log("productView signal:", JSON.stringify(signal, null, 2));

                    // Send signal to Connect
                    TLT.logSignal(signal);
                });
            }
        }
    });
}

Best practices

  1. Capture all product displays - Fire signals for product detail pages, quick views, and any other context where products are viewed.
  2. Include category information - productCategory is crucial for segmentation and analytics.
  3. Handle multiple products carefully - When showing multiple products (like hotel rooms), send one signal per product.
  4. Use consistent identifiers - Ensure productId matches what you use in other product signals (add-to-cart, order, product configuration).
  5. Use optional fields - Fields like availability, productRating, and inventoryQuantity help keep the product catalog up-to-date..

Troubleshooting

Signal not firing on product pages?

  • Verify the Connect library is initialized before firing signals.
  • Check that required fields are all populated.
  • Confirm your page detection logic correctly identifies product pages.
  • Use console.log() to verify the signal object before sending.

Missing product data?

  • Check if elements exist in DOM when the script runs.
  • Verify selectors match your site's HTML structure.
  • Consider using data layer if available for more reliable data.

Signals firing on non-product pages?

  • Review your page detection logic (URL patterns, DOM element checks).
  • Ensure conditional checks are specific enough.
  • Test on various page types to verify correct triggering.

Multiple signals for single product?

  • Check if script is running multiple times on page load.
  • Verify you're not attaching multiple event listeners.
  • Consider debouncing or adding flags to prevent duplicates.

Related pages

How behavior signals are processed in Connect