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:
- Checkout/cart page - Collect product details, quantities, prices.
- Order placement - Store collected data in session storage.
- 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 signaleffect: 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 sendingpositivefor 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 orderorderId(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 aquantityof 3. ⚠️ IforderedItemsfails validation, the entire signal will be discarded.orderShippingHandling: Number - Shipping and handling costorderSubtotal: Number - Subtotal amount (net of discount)orderTax: Number - Tax amountorderValue: Number - Total order value (including tax and shipping)signalCustomAttributes: Array of objects - Additional custom attributes. Each object hasnameandvaluestring fields.
Ordered items object
discount: Number - Discount amount per unitimageUrls: Array of strings - URLs of product imagesitemQuantity: Number - Quantity of this product purchasedproductCategory: String - Product category from catalogproductCategoryId: String - Product category identifierproductDescription: String - Description of the productproductId(required): String - Unique product identifier (may match SKU)productName(required): String - Name of the productproductUrls: Array of strings - URLs of product pagespromotionId: String - ID from marketing campaigns that influenced the purchaseshoppingCartUrl: String - URL of the shopping cartunitPrice(required): Number - Price the customer paid for one unitvirtualCategory: 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
Updated 6 days ago
