Enable add-to-cart signals in the Connect web library
The add-to-cart signal captures when a user selects a product, service, or offering for purchase. This signal helps track shopping behavior across your website and can be used for product recommendations and behavioral analytics.
Availability: Pro, Premium and Ultimate
Implementation considerations
Button variations
Different button styles may require different selectors or extraction logic. Consider:
- Buttons with different CSS classes or IDs
- Buttons that use different HTML structures
- Buttons that store product data in different attributes or locations

Two variations of the "Buy" button at Potgang.co.uk

Three variations of the "Buy" button at Amazon.com
You may need separate code paths to handle each variation.
Triggering the signal
Decide when to send the signal based on your site's behavior:
- Immediate (on click) - Fire the signal as soon as the user clicks Add to cart. Best for tracking intent, even if the action fails.
- Delayed (on cart update) - Fire the signal after confirming the item was added to the cart. Ensures accuracy but may miss failed attempts.
Multiple purchasing flows
You may need separate pieces of logic to support all available purchasing flows, such as:
- Adding from the product detail page
- Adding from the product listing page

The "Quick add" button in a product list at Beefcakeswimwear.com
- Adding from a product modal or quick view
- Bundle or group offers (you can send individual or bundled signals for them)

Product grouping at Amazon.com
Each flow may structure product data differently in the DOM.
Data layer
If your website uses a data layer (for example, Google Tag Manager), it will simplify extracting product information. Look for structured product objects that include ID, name, price and other relevant attributes.
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.
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: Object - Key-value pairs mapping the signal to a contact and optionally updating contact attributes. Keys must match attribute names exactly as they appear in Connect, including capitalization and spacing.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,smsorwhatsapp.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. Usepositivefor all add-to-cart signals.name: String - Name to differentiate this signal from others (example: "addToCart from quick-add" vs "addToCart from PDP"). Max length - 256 chars.signalType(required): String - Valid value -addToCart.
Audience object
Create a flat key-value object where each key is a contact attribute name and each value is the new value for that attribute. Include at least one identifier for contact mapping (recommended) and any number of additional pairs to update other contact attributes.
Important:
The audience object must be a flat key-value object — not an array of name/value pairs. If the format doesn't match, the contact will not be created or updated.
Note
- Attribute keys must match exactly how they appear in Connect — including capitalization and spacing. Use the Contact attributes query to look up attribute names.
- The format of each value depends on the attribute type: text, number, Boolean, or date.
- 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.- Sending an empty or null value is ignored — the contact attribute will not be updated.
Consent object
Use the consent object to update a contact's opt-in status. Each channel accepts multiple entries to set different consent statuses per consent group in a single call.
enableOverrideExistingOptOut(required): Boolean - Whentrue, current opt-out statuses will be overridden by new statuses. Whenfalse, 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
-
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 an add-to-cart signal comes without a value, the system will fall back on the default currency set for your Connect subscription. -
discount: Number - Discount amount or difference between original and current price -
imageUrls: Array of strings - The URLs of product images -
itemQuantity(required): Number - Quantity of items added to the cart -
productCategory: String - Product category from your catalog (useful for segmentation) -
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 - The URLs of product pages -
promotionId: String - ID from marketing campaigns (hero images, CTAs) that led to this action -
shoppingCartUrl: String - URL of the shopping cart -
signalCustomAttributes: Array of objects - Additional custom attributes. Each object hasnameandvaluestring fields. -
unitPrice(required): Number - Unit price of the product -
virtualCategory: String - Category based on navigation path (e.g., "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.
- If a required field isn't applicable to your business, use a placeholder value.
Basic example
// Initialize the Connect library
if (window.TLT) {
TLT.initPremium({
appKey: "APP_KEY",
postUrl: "COLLECTOR_URL",
callback: function() {
const signal = {
// Required fields
signalType: "addToCart",
productId: "SKU-12345",
productName: "Wireless Headphones",
itemQuantity: 1,
unitPrice: 299.99,
// Optional but recommended fields
name: "addToCart from product page",
category: "Behavior",
productDescription: "Premium noise-cancelling headphones",
discount: 30.00,
currency: "USD",
effect: "positive",
productCategory: "Electronics",
productUrls: ["https://example.com/products/headphones"],
imageUrls: ["https://example.com/images/headphones.jpg"],
shoppingCartUrl: "https://example.com/cart",
// Contact mapping
audience: {
"Customer ID": "AAUN-1417508"
}
};
// Send the signal
TLT.logSignal(signal);
}
});
}
Complete implementation example
This example handles add-to-cart events from both product detail pages and quick-add buttons in product listings. The audience identifier is retrieved from a browser cookie. A marketing opt-in checkbox is used to conditionally update email consent.
Be cautious when updating consent
The
consentobject in the example below is used to update the opt-in status of a contact. When using this feature, make sure you involve all stakeholders to fully understand the ways a contact can opt out and how that status is maintained in the audience so that an opt-out status cannot be overwritten unintentionally, for example when audience data is batch loaded from another system.You should also ensure that your code accounts for corner cases such as the customer checking and then unchecking the consent checkbox before submitting the form.
// Utility function to read a cookie by name
function getCookie(name) {
const match = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
return match ? decodeURIComponent(match[2]) : "";
}
// Initialize the Connect library
if (window.TLT) {
TLT.initPremium({
appKey: "APP_KEY",
postUrl: "COLLECTOR_URL",
callback: function() {
// Define the signal template
const signal = {
// Required fields
signalType: "addToCart",
productId: "",
productName: "",
itemQuantity: 1,
unitPrice: 0,
// Optional fields
name: "addToCart generated by website",
category: "Behavior",
currency: "USD",
effect: "positive",
productDescription: "",
discount: 0,
productCategory: "",
productUrls: [],
imageUrls: [],
shoppingCartUrl: "https://" + window.location.host + "/shopping-cart",
virtualCategory: "",
promotionId: ""
};
// Add contact mapping from cookie
const customerId = getCookie("customerId");
if (customerId) {
signal.audience = {
"Customer ID": customerId
};
}
// Define selectors for different add-to-cart button types
const addToCartButtons = [{
selector: "#add-to-cart",
type: "Product details page"
},
{
selector: ".quick-add-button-rounded",
type: "Quick add button"
}
];
// Attach click listeners to all matching elements
addToCartButtons.forEach((addToCartButton) => {
const elements = document.querySelectorAll(addToCartButton.selector);
if (!elements.length) return;
elements.forEach((element) => {
element.addEventListener("click", () => {
// Add consent update if the marketing opt-in checkbox is checked
const marketingOptIn = document.querySelector("#marketingOptIn")?.checked;
if (marketingOptIn) {
signal.consent = {
enableOverrideExistingOptOut: false,
email: [
{
status: "OPT_IN"
}
]
};
}
// Handle product details page button
if (addToCartButton.type === "Product details page") {
const article = document.querySelector(".product--detailPage");
if (article) {
signal.productId = article.getAttribute("data-sku");
signal.productName = article.getAttribute("data-name");
const productUrl = article.getAttribute("data-url");
if (productUrl) {
signal.productUrls = [productUrl];
}
const imageUrl = article.getAttribute("data-image-url");
if (imageUrl) {
signal.imageUrls = [imageUrl];
}
signal.itemQuantity = Number(article.querySelector(".cartItem--success")?.innerText) || 1;
const discountedPrice = Number(article.querySelector(".price-sale")?.innerText) || 0;
const regularPrice = Number(article.querySelector(".price-default")?.innerText) || 0;
if (discountedPrice) {
signal.unitPrice = discountedPrice;
signal.discount = Math.round((regularPrice - discountedPrice) * 100) / 100;
} else {
signal.unitPrice = regularPrice;
}
console.log("addToCart signal:", JSON.stringify(signal, undefined, 2));
TLT.logSignal(signal);
}
}
// Handle quick add button
if (addToCartButton.type === "Quick add button") {
const article = element.closest(".product-tile");
if (article) {
signal.productId = article.querySelector(".thumbnail_code")?.innerText || "";
signal.productName = article.querySelector(".thumbnail_name")?.innerText || "";
const productUrl = article.querySelector(".thumbnail_link")?.href;
if (productUrl) {
signal.productUrls = [productUrl];
}
const imageUrl = article.querySelector(".thumbnail_image")?.src.split("?")[0];
if (imageUrl) {
signal.imageUrls = [imageUrl];
}
signal.itemQuantity = 1;
const discountedPrice = Number(article.querySelector(".price-sale")?.innerText) || 0;
const regularPrice = Number(article.querySelector(".price-default")?.innerText) || 0;
if (discountedPrice) {
signal.unitPrice = discountedPrice;
signal.discount = Math.round((regularPrice - discountedPrice) * 100) / 100;
} else {
signal.unitPrice = regularPrice;
}
console.log("addToCart signal:", JSON.stringify(signal, undefined, 2));
TLT.logSignal(signal);
}
}
});
});
});
}
});
}
Troubleshooting
Signal not firing on click?
- Verify the Connect library is initialized before attaching event listeners.
- Check that your selectors match the actual elements in the DOM.
- Ensure no JavaScript errors prevent the listener from attaching.
Product data missing or incorrect?
- Confirm product attributes are present in the DOM at the time of the click.
- Verify data-* attribute names match your site's HTML structure.
- Use console.log() to inspect the signal object before sending.
Contact not created or updated?
- Verify the audience field is a flat key-value object — if the format doesn't match, the contact will not be created or updated.
- Confirm attribute key names match exactly how they appear in Connect — including capitalization and spacing.
- Confirm the attribute value type matches the field type defined in Connect (text, number, Boolean, or date).
Related pages
Updated 8 days ago
