Enable order signals in the Connect web library

The order signal captures when a user completes a transaction on your website. This signal is essential for tracking conversions, calculating revenue, and triggering post-purchase campaigns.

Typical use cases across business domains:

  • Retail: Order confirmation
  • Travel: Booking confirmation
  • Banking/Insurance: Application confirmation

Availability: Pro, Premium and Ultimate


Implementation considerations

Order confirmation challenge

The order signal is one of the most complex to implement because order details are rarely all available on the order confirmation page. Key data points are often only accessible earlier in the checkout process, but you cannot fire the signal until an order ID is generated.

Recommended approach: Store partial order data in the browser session storage during checkout, then complete and send the signal once the order is confirmed. Delete the signal from the storage after that.

Data collection strategy

Plan your implementation around these stages:

  1. Checkout/cart page - Collect product details, quantities, prices.
  2. Order placement - Store collected data in session storage.
  3. Confirmation page - Retrieve stored data, add order ID, send signal.

Product catalog integration

Your company's product catalog in Connect is dynamically updated based on incoming order signals from your website.

  • If a signal contains a new product ID or category ID, Connect creates a new catalog entry based on the signal content.
  • If a product ID is already present in the catalog, Connect makes sure the default product attributes are up-to-date with the signal (name, unit price, category, description, currency, discount, product URLs).
  • If a category ID is already present in the catalog, Connect makes sure its name matches the signal content.

Special handling:

  • Null values in the signal do not overwrite existing catalog data.
  • Blank values (“”) update the catalog to blank/null.
  • Zero values update the related catalog field to zero.

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

  • category: String - Valid value - Behavior.
  • description: String - Description of the signal
  • effect: String - Describes the effect of the signal on engagement. It is intended to be used for engagement index scoring. Valid values: negative, positive. We suggest sending positive for all order signals.
  • name: String - Name to differentiate this signal from others (for example, "Order from website"). Max length - 256 chars.
  • signalType (required): String - Valid value - order.

Signal content

  • currency (required): 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.
  • orderDiscount: Number - Total discount applied to the order
  • orderId (required): String - Unique order identifier (usually scraped from URL or page content)
  • orderedItems: Array of objects - List of ordered items (see structure below). You need a separate object for each product, but not each instance of the same product - three identical items would result in one object, with a quantity of 3. ⚠️ If orderedItems fails validation, the entire signal will be discarded.
  • orderShippingHandling: Number - Shipping and handling cost
  • orderSubtotal: Number - Subtotal amount (net of discount)
  • orderTax: Number - Tax amount
  • orderValue: Number - Total order value (including tax and shipping)
  • signalCustomAttributes: Array of objects - Additional custom attributes. Each object has name and value string fields.

Ordered items object

  • discount: Number - Discount amount per unit
  • imageUrls: Array of strings - URLs of product images
  • itemQuantity: Number - Quantity of this product purchased
  • 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
  • productUrls: Array of strings - URLs of product pages
  • promotionId: String - ID from marketing campaigns that influenced the purchase
  • shoppingCartUrl: String - URL of the shopping cart
  • unitPrice (required): Number - Price the customer paid for one unit
  • virtualCategory: String - Category based on navigation path (e.g., "New arrivals", "Sale")

Note:

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

Basic example

const signal = {
    // Required order-level fields
    signalType: "order",
    name: "order from website",
    category: "Behavior",
    orderId: "ORD-123456",
    currency: "USD",
    effect: "positive",

    // Optional order-level fields
    orderSubtotal: 299.99,
    orderTax: 24.00,
    orderShippingHandling: 10.00,
    orderDiscount: 30.00,
    orderValue: 303.99,

    // Ordered items array
    orderedItems: [
        {
            // Required item-level fields
            productId: "SKU-12345",
            productName: "Wireless Headphones",
            itemQuantity: 1,
            unitPrice: 299.99,
            currency: "USD",

            // Optional item-level fields
            discount: 30.00,
            productCategory: "Electronics"
        }
    ]
};

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

Complete implementation example

This example demonstrates collecting order data during checkout and sending the complete signal on the confirmation page.

// Utility function to write data to session storage
function store(key, value) {
    const storeKey = "aco-store";

    if (!sessionStorage) {
        console.warn("Session storage unavailable");
        return false;
    }

    let acoStore = sessionStorage.getItem(storeKey) || "{}";
    acoStore = JSON.parse(acoStore);
    // If a signal is being passed in
    if (typeof key === "object" && key.signalType && value === undefined) {
        // If a key for this signalType exists
        if (acoStore[key.signalType]) {
            acoStore[key.signalType].push(key);
        } else {
            acoStore[key.signalType] = [key];
        }
    }
    if (key && value !== undefined) {
        acoStore[key] = value;
    }
    sessionStorage.setItem(storeKey, JSON.stringify(acoStore));
    return true;
}

// Utility function to read data from session storage
function retrieve(key, del) {
    const storeKey = "aco-store";

    if (!sessionStorage) {
        console.warn("Session storage unavailable");
        return false;
    }

    let acoStore = sessionStorage.getItem(storeKey) || "{}";
    acoStore = JSON.parse(acoStore);
    const value = acoStore[key] || "";
    if (del && del === "delete") {
        delete acoStore[key];
        if (Object.keys(acoStore).length === 0) {
            sessionStorage.removeItem(storeKey);
        } else {
            sessionStorage.setItem(storeKey, JSON.stringify(acoStore));
        }
    }
    return value;
}

// Check that the Connect SDK is present
if (window.TLT && window.TLT.isInitialized()) {
    const signal = {
        signalType: "order",
        name: "order generated by website",
        category: "Behavior",
        orderId: "",
        orderSubtotal: 0,
        orderValue: 0,
        currency: "USD",
        orderShippingHandling: 0,
        orderTax: 0,
        orderDiscount: 0,
        orderedItems: [],
        effect: "positive",
        signalCustomAttributes: []
    };
    const href = window.location.href;

    // On the "Place Order" screen, before order confirmation
    if (href.includes("/place-order")) {
        const rows = document.querySelectorAll("tr.lineItem");

        // Iterate over the table of products and populate orderedItems array
        rows.forEach((row) => {
            const orderedItem = {
                productId: "",
                productName: "",
                productDescription: "",
                itemQuantity: 0,
                unitPrice: 0,
                currency: "USD",
                discount: 0,
                productCategory: "",
                productUrls: [],
                imageUrls: [],
                shoppingCartUrl: "https://" + window.location.host + "/shopping-cart",
                promotionId: "",
                virtualCategory: "",
                actionState: "",
                resumedCartItem: ""
            };

            orderedItem.productId = row.querySelector(".lineItem_sku").innerText || "";
            orderedItem.productName = row.querySelector(".lineItem_name").innerText || "";
            orderedItem.itemQuantity = Number(row.querySelector(".lineItem_qty").innerText) || 0;

            const productUrl = row.querySelector(".lineItem_nameLink").href || "";
            if (productUrl) {
                orderedItem.productUrls.push(productUrl);
            }

            const imageUrl = row.querySelector(".lineItem_image > img").src.split("?")[0] || "";
            if (imageUrl) {
                orderedItem.imageUrls.push(imageUrl);
            }

            const discountedPrice = Number(row.querySelector(".price_sale").innerText) || 0;
            const regularPrice = Number(row.querySelector(".price_default").innerText) || 0;

            if (discountedPrice) {
                orderedItem.unitPrice = discountedPrice;
                orderedItem.discount = Math.round((regularPrice - discountedPrice) * 100) / 100;
            } else {
                orderedItem.unitPrice = regularPrice;
            }

            signal.orderedItems.push(orderedItem);
        });

        // Scrape the total prices from order summary
        signal.orderSubtotal = Number(document.querySelector("div.orderSummary_subtotal").innerText) || 0;
        signal.orderValue = Number(document.querySelector("div.orderSummary_total").innerText) || 0;
        signal.orderShippingHandling = Number(document.querySelector("div.orderSummary_shipping").innerText) || 0;

        // Store the signal until we have the order confirmation number
        store(signal);
    }

    // On the "Order Confirmation" screen
    if (href.includes("OrderConfirmation/?OrderNumber=")) {
        const orderSignal = retrieve("order", "delete");
        orderSignal.orderId = href.split("OrderConfirmation/?OrderNumber=")[1];

        // If we have a stored signal, an orderId, and ordered items, send the signal
        if (orderSignal && orderSignal.orderId && orderSignal.orderedItems.length) {
            console.log("order signal:", JSON.stringify(orderSignal, undefined, 2));
            window.TLT.logSignal(orderSignal);
        }
    }
}

Troubleshooting

Signal not capturing all products?

  • Verify your selector matches all product rows in the checkout table
  • Check that the DOM structure is consistent across all product types
  • Ensure the loop properly iterates over all items

Missing order details on confirmation page?

  • Confirm data is being stored to session storage on the checkout page
  • Verify session storage persists between page loads (check browser settings)
  • Test that the retrieve function is finding the stored data

Order ID not extracted correctly?

  • Check the URL pattern on your confirmation page
  • Verify your string splitting logic matches the actual URL format
  • Consider using DOM selectors as alternatives

Signal sent multiple times?

  • Ensure you're deleting the signal from storage after sending
  • Check that page refresh doesn't re-trigger the signal logic
  • Consider adding a flag to prevent duplicate sends