Enable wishlist-item-removed signals in the Connect web library

The wishlist-item-removed signal fires when a user removes a product from their wishlist or list of saved items. This signal helps you understand which saved products lose their appeal, spot early signs of fading purchase intent, and time win-back campaigns before a customer loses interest completely.

This signal tracks details about products that customers remove from their wishlist, including product identifiers, pricing and wishlist context. By capturing this data, you can:

  • Identify products with high save-then-abandon rates
  • Trigger win-back campaigns for recently removed items
  • Compare wishlist adds and removes to see which saved products convert
  • Feed product-level intent data into your broader view of purchase behavior, alongside add-to-cart and removed-from-cart signals

Availability: Pro, Premium and Ultimate



Implementation considerations

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 the remove or un-save control. Best for tracking intent, even if the action fails.
  • Delayed (on confirmation) - Fire the signal after confirming the item was removed from the wishlist. Ensures accuracy but may miss failed attempts.

Removal locations

Users can remove items from a wishlist from different locations on your site, depending on your design:

  • Wishlist or favorites page - the main view where users manage saved items
  • Product card - a "remove" control or a toggled heart/star icon shown directly on a product tile
  • Product detail page - a saved-item indicator that can be un-toggled
  • Mini-wishlist or wishlist widget - quick access from the header or sidebar

Each location may have different HTML structures and require different selectors.

Removal interactions

Different removal patterns may require different handling:

  • Remove button - an explicit "Remove" or "Delete" control on the wishlist page
  • Un-save toggle - clicking a filled heart or star icon to un-save an item
  • Move to cart - some sites remove an item from the wishlist as a side effect of adding it to the cart; decide whether this action should also fire the signal
  • Clear wishlist - removing all saved items at once (send an individual signal for each item)

Data availability

Consider how product data is available when an item is removed:

  • From the DOM - product information is visible in the wishlist UI (easiest approach)
  • From application state - your application maintains a wishlist data object
  • From a data layer - wishlist events push data to a data layer (for example, Google Tag Manager)

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, sms or whatsapp.
  • 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. Use negative for all wishlistItemRemoved signals.
  • name: String - Name to differentiate this signal from others (example: "wishlistItemRemoved from wishlist page" vs "wishlistItemRemoved from product card"). Max length - 256 chars.
  • signalType (required): String - Valid value - wishlistItemRemoved.

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 - 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 fields

  • 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 wishlistItemRemoved signal comes without a value, the system will fall back on the default currency set for your Connect subscription.
  • externalVariantId: String - Identifier for the specific product variant removed, such as a size or color
  • price: Number - Price of the product at the time it was removed from the wishlist
  • productCategory: String - Product category name based on catalog
  • productId (required): String - Unique product identifier (may match SKU)
  • productName: String - Name of the product
  • wishlistId: String - Identifier for the wishlist the product was removed from, useful if a contact can maintain more than one wishlist

Notes:

  • If productId 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: "wishlistItemRemoved",
        productId: "SKU-98234",
        // Optional but recommended fields
        name: "wishlistItemRemoved from wishlist page",
        category: "Behavior",
        effect: "negative",
        productName: "Women's Running Shoe",
        productCategory: "Footwear",
        price: 89.99,
        currency: "USD",
        wishlistId: "wl-001",
        // Contact mapping
        audience: {
          "Customer ID": "AAUN-1417508"
        }
      };
      // Send the signal
      TLT.logSignal(signal);
    }
  });
}

Complete implementation example

This example handles removal events from both the wishlist page and a product-card un-save toggle. The audience identifier is retrieved from session storage, where it was stored during login.

// 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: "wishlistItemRemoved",
        productId: "",
        // Optional fields
        name: "wishlistItemRemoved generated by website",
        category: "Behavior",
        effect: "negative",
        currency: "USD",
        productName: "",
        productCategory: "",
        price: 0,
        wishlistId: ""
      };

      // Add contact mapping from session storage
      const customerId = sessionStorage.getItem("customerId");
      if (customerId) {
        signal.audience = {
          "Customer ID": customerId
        };
      }

      // Define selectors for different remove controls
      const removeControls = [
        { selector: ".wishlist-item__remove", type: "Wishlist page" },
        { selector: ".product-card__unsave-btn", type: "Product card" }
      ];

      // Attach click listeners to all matching elements
      removeControls.forEach((removeControl) => {
        const elements = document.querySelectorAll(removeControl.selector);
        if (!elements.length) return;

        elements.forEach((element) => {
          element.addEventListener("click", () => {
            // Find the wishlist item container
            const wishlistItem = element.closest(".wishlist-item") ||
              element.closest(".product-card");
            if (!wishlistItem) return;

            // Extract product information from the item
            signal.productId = wishlistItem.getAttribute("data-product-id") ||
              wishlistItem.querySelector(".product-id")?.innerText || "";
            signal.productName = wishlistItem.getAttribute("data-product-name") ||
              wishlistItem.querySelector(".product-name")?.innerText || "";
            signal.wishlistId = wishlistItem.getAttribute("data-wishlist-id") || "";

            const priceElement = wishlistItem.querySelector(".price-current") ||
              wishlistItem.querySelector(".product-price");
            signal.price = priceElement ?
              parseFloat(priceElement.innerText.replace(/[^0-9.]/g, "")) : 0;

            const categoryElement = wishlistItem.querySelector(".product-category");
            if (categoryElement) {
              signal.productCategory = categoryElement.innerText;
            }

            // Set removal location
            signal.name = `wishlistItemRemoved from ${removeControl.type}`;

            console.log("wishlistItemRemoved signal:", JSON.stringify(signal, undefined, 2));
            TLT.logSignal(signal);
          });
        });
      });
    }
  });
}

Best practices

  1. Capture product data before the DOM element disappears
  2. Fire one signal per item removed, even for "clear wishlist" actions
  3. Keep currency consistent across signals
    Always include an audience identifier — pairing removals with adds is what makes win-back analysis work
  4. Omit price/currency rather than sending placeholders

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.
  • Test on both the main wishlist page and any product-card or widget variations.

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).


Did this page help you?