Welcome to the Retention Implementation Guide for your custom site. This document will make installing our lightweight JavaScript code snippet into your site easy and fun, enabling our robust suite of tools to run in the background for you.
Are you using a popular eCommerce platform like Shopify, WooCommerce, or BigCommerce? Check out our unique platform guides here: Platform Guides
The objective of this guide is to provide you with clear, step-by-step instructions to ensure a smooth and efficient setup process. By following this guide, you will enable "Grow" to identify anonymous traffic on your site and "Reclaim" to retarget known who browse anonymously.
Should you encounter any difficulties or have questions during the implementation process, our dedicated support team is available to assist you. You can reach out directly to your Implementation manager, or via email at support@retention.com. You can also use our Messenger tool by logging into your R! account and clicking the icon in the lower right-hand corner of the screen.
Before you begin the implementation process, ensure that the following prerequisites are met:
The code snippet is the backbone of Retention.com’s product functionality. It's essential that this snippet is present on every page of your website where our functions will be used.
The code snippet should be placed within the <head> section of your website's HTML. This ensures that the snippet is one of the first things to load.
Copy the entire code snippet provided below:
<script type='text/javascript'>!function(){var geq=window.geq=window.geq||[];if(geq.initialize||geq.invoked){if(window.console)console.error('GE snippet included twice.');return;}
//Get this from your Retention Implementation Manager
var retention_account_id ='{R! SCRIPT KEY}';
geq.invoked=true;var methods=['page','suppress','trackOrder','identify','addToCart','callBack','event'];for(var i=0;i<methods.length;i++){var key=methods[i];geq[key]=function(method){return function(){var args=Array.prototype.slice.call(arguments);args.unshift(method);geq.push(args);return geq;};}(key);}geq.load=function(key){var script=document.createElement('script');script.type='text/javascript';script.async=true;script.src='https://s3-us-west-2.amazonaws.com/jsstore/a/'+key+'/ge.js'+(location.href.includes('vge=true')?'?v='+Math.random():'');var firstScript=document.getElementsByTagName('script')[0];firstScript.parentNode.insertBefore(script,firstScript);};geq.SNIPPET_VERSION='1.6.1';geq.load(retention_account_id);}();</script>
// (HIGHLY RECOMMENDED) Add this to include our grow product
<script> geq.page() </script>
More details on how to do so can be found here.
Avoid Duplicate Snippets: Ensure the snippet is included only once within the <head> section.
Once the snippet is in place, conduct a thorough test to ensure it’s functioning as intended:
NOTE: Please ensure the code snippet has been installed first.
Grow is designed to identify anonymous audiences on your website and requires a single function call to be made on every page, as well as an additional tracking script on the order completion page.
NOTE: If you included the <script> geq.page() </script> in the code snippet function above, you can skip this step.
The `geq.page()` function is crucial as it determines the identity of the person landing on your website. Note: we recommend that this be set right under the code snippet. If you copied the code from the code snippet above, then this will already be completed.
Insert the Function: Immediately after the code snippet, insert the `geq.page` function call.
<script type="text/javascript">
// ...Your Retention code snippet...
geq.page();
</script>
This step is OPTIONAL for advanced set-ups!
If available, pass an object with an identifier (user ID or session ID) to the `geq.identify()` function before calling page. This identifier should be generated by your website's backend systems and is useful for correlating the anonymous visitor with a known contact if they convert.
Note: The property must be passed as an object with a property called “user_id”.
<script type="text/javascript">
// ...Your Retention code snippet must be present on the page...
// Ensure the ID is set BEFORE calling geq. functions . Replace with your user ID variable
var userIdentifier = { user_id: 'USER_ID_FROM_YOUR_SYSTEM' };
// Call this to set-up your identifier
geq.identify(userIdentifier);
// Then call the page function
geq.page();
</script>
The order tracking script is used to track the success of Grow by reporting conversions back to our system. We automatically deduplicate if we receive information about the same order more than once.
1. Locate Order Completion Page: Find the HTML or template file for your order completion or 'Thank You' page.
2. Insert Track Order Function: On this page, insert the `geq.trackOrder` function call, replacing placeholders with actual order variables generated by your e-commerce system.
<script type="text/javascript">
geq.trackOrder({
order_number: "{{ ORDER_NUMBER }}", // Replace with your order number variable
order_amount: "{{ ORDER_AMOUNT }}", // Replace with your order amount variable
order_email: "{{ CUSTOMER_EMAIL }}" // Replace with your customer email variable
});
</script>
Note: Replace the `{{ ORDER_NUMBER }}`, `{{ ORDER_AMOUNT }}`, and `{{ CUSTOMER_EMAIL }}` placeholders with actual data from your e-commerce platform's order completion variables.
3. Verify the Data: Make sure that the variables for the order number, amount, and customer email are dynamically populated with real data for each transaction.
4. Test the Functionality: After placing the script, test a transaction to ensure the script captures and sends the data correctly.
NOTE: We automatically suppress many popular pop-up forms! You do not need to take any action if your pop-up forms are powered by: JustUno, Klaviyo, Omnisend, OptinMonster, PostScript, Privy, SendLane, Shopify’s footer.
The `geq.suppress` function should be called whenever a user voluntarily submits their email on your site. This ensures that the Grow identification script does not re-collect their information.
1. Identify Email Submission Points: Determine where on your site users can enter their email (e.g., newsletter sign-up, pop-up, etc.).
2. Insert Suppress Function: In the event handler for the email submission action, insert a call to `geq.suppress`.
3. Ensure Proper Timing: The suppress function should be triggered immediately upon the successful submission of an email.
// Include this function
geq.suppress();
Test all of the functions you’ve included!
Reclaim is designed to retarget audiences browsing your website that show an intent to purchase, but abandon before they convert. The events we identify and send to you enable you to send targeted abandonment email campaigns.
The placeholders in the functions (i.e. name: "Product Name") indicate where dynamic content should be inserted. When implementing the functions, these placeholders would be replaced by the actual product details, typically using server-side rendering or a templating system that fills in these details from the order processing system.
This will be unique to your system. It may help to look for other areas where these properties are used on your site as a reference.
To capture when a user adds a product to their cart, you will need to trigger the geq.addToCart event with the appropriate product details.
<script type="text/javascript">
//NOTE: Labels can be changed per your requirements. You can add/removed fields
//NOTE: Fill our the values for these fields with dynamic variables from your system; these are often server-side rendering or templates.
var atcitem = {
name: "Product Name", // Replace with dynamic product name
price: "Product Price", // Replace with dynamic product price
productID: "Product ID", // Replace with dynamic product ID
categories: "Product Categories", // Replace with dynamic product categories (optional)
imageURL: "Product Image URL", // Replace with dynamic product image URL
URL: "Product Page URL", // Replace with dynamic product page URL
brand: "Product Brand" // Replace with dynamic product brand (optional)
};
if (window.location.href.includes('vge=true')) { console.log('Add To Cart Item:', atcitem);}
// Trigger the addToCart event with the latest product data
if (typeof geq !== 'undefined' && typeof geq.addToCart === 'function') {
geq.addToCart(atcitem);}
</script>
Notes:
When a user views a product, you must capture the details of the product they are viewing and pass this as an object to the geq.event function.
<script type="text/javascript">
//NOTE: Labels can be changed per your requirements. You can add /remove additional fields
//NOTE: Fill our the values for these fields with dynamic variables from your system; these are often server-side rendering or templates.
var vpitem = {
name: "Product Name", // Replace with dynamic product name
price: "Product Price", // Replace with dynamic product price
productID: "Product ID", // Replace with dynamic product ID
categories: "Product Categories", // Replace with dynamic product categories (optional)
imageURL: "Product Image URL", // Replace with dynamic product image URL
URL: "Product Page URL", // Replace with dynamic product page URL
brand: "Product Brand" // Replace with dynamic product brand (optional)
};
if (window.location.href.includes('vge=true')) {
console.log('Viewed Product Reclaim', vpitem);
}
// Trigger the event with the product data
if (typeof geq !== 'undefined' && typeof geq.event === 'function') {
//Note: You can change the first argument to anything you need
geq.event('Viewed Product Reclaim', vpitem);}
</script>
Notes:
While less emphasized, capturing when a user views a category may provide additional insights for retargeting purposes. This is a very uncommon feature and is 100% optional.
Locate Viewed Category Script: Implement a similar trigger as the product view on category pages.
Category Event Code: Use the geq.event function to track when a category is viewed.
<script type="text/javascript">
var categoryItem= {
name: "title of the category" //replace with dynamic category name
url: location.href //URL of the current page
};
geq.event('Viewed Category Reclaim', categoryItem);
</script>
For businesses already using Klaviyo to track customer interactions, installing Reclaim is even easier! While Klaviyo captures a subset of your traffic, Reclaim is designed to recognize a larger portion, making your retargeting campaigns even more powerful.
NOTE: If you have installed Klaviyo tracking through the use of a direct Klaviyo Integration (i.e. plugin) then this option is not available. Only customers who can adjust their Klaviyo code can utilize the following.
NOTE: You may still be able to use the var item that Klaviyo creates in your browser for Retention’s events. If the geq.event('Viewed Product Reclaim', item); & geq.addToCart(item); code appears/occurs AFTER Klaviyo’s var item is set up, then you can utilize this variable for our events.
Klaviyo uses a JavaScript API to track customer behavior on a website, which typically involves using a function called _learnq. If you already have installed Klaviyo tracking, then simply add the geq.addToCart & geq.Event Functions to the same part of your code!
The general pattern of adding our code to Klaviyo's code snippet requires identifying where you set-up your Klaviyo _learnq script (i.e. _learnq.push(["track", "Viewed Product", item]);).
// Look for your Klaviyo implementation - which will look something like:
{% if product %}
<script type="text/javascript">
var _learnq = _learnq || [];
var item = {
"ProductName": "{{ product.title }}",
"ProductID": "{{ product.id }}",
"SKU": "{{ product.sku }}",
"Categories": {{ product.categories | json }},
"ImageURL": "{{ product.image.src }}",
"URL": "{{ shop.url }}{{ product.url }}",
"Brand": "{{ product.vendor }}",
"Price": {{ product.price | money_without_currency }},
"CompareAtPrice": {{ product.compare_at_price | money_without_currency }}
};
_learnq.push(["track", "Viewed Product", item]);
// Add Retention's View Product Event call here
geq.event('Viewed Product Reclaim', item);
</script>
{% endif %}
//An example of Klaviyo's add to cart script
<script type="text/javascript">
document.querySelector('ProductForm__AddToCart').addEventListener('click',function (){
_learnq.push(['track', 'Added to Cart', item]);
// Add Retention's Add To Cart Event call here
// Method 1: (EASY) Use the same item as Klaviyo
geq.addToCart(item);
// Method 2: (ADVANCED) Customize the item
// NOTE: you can edit the payload if you need to
if (typeof geq !== 'undefined' && typeof geq.addToCart === 'function') {
geq.addToCart({
name: item.ProductName,
price: item.Price,
productID: item.ProductID,
categories: item.Categories,
imageURL: item.ImageURL,
URL: item.URL,
brand: item.Brand,
CompareAtPrice: item.CompareAtPrice});}
});
</script>
Apart from the standard events, you can track custom events unique to your business.
Please note this is for advanced setups and is not used for the majority of customers. If you are not sure if you need this, it's likely you can skip this sub-section entirely.
// Create a custom object to hold the details you need
Var customItem = {
action: 'User Action',
details: 'Additional Details'
};
// You can call the event anything you desire
// NOTE: There is a 4 minute delay on an calls to geq.event
geq.event('Custom Event Name', item);
Q: What is the “code snippet”, and why is it important?
The code snippet is a block of JavaScript code provided to you by your Implementation Manager. This code initializes the Grow and Reclaim products on your website. It's essential because it defines the functions that you will call to track user behavior. Without it, the tracking functions like geq.page or geq.addToCart won't work.
Q: Why does the code snippet need to be on every page?
For Grow and Reclaim to track user actions across your entire site, the code snippet needs to be present on every page. This ensures that regardless of where the user navigates, their actions can be captured and analyzed.
Q: Why must the code snippet be placed before the geq function calls?
Order matters in JavaScript because the code is executed in the sequence it appears on the page. If you try to call geq functions before the code snippet, you'll get an error because the browser doesn't yet recognize what geq is. Placing the code snippet first allows it to set everything up, so when geq functions are called, they work correctly.
Q: Can I just place the snippet at the bottom of my pages?
It's recommended to place the snippet within the <head> tag or near the top of the <body> tag to ensure it loads early. This is crucial for tracking user actions from the moment they arrive on the page. If the snippet is placed at the bottom, some user actions might not be tracked if the user interacts with the page before the snippet has loaded.
Q: How do I test to make sure everything is working?
Q: Will adding this JavaScript slow down my website?
The code snippet and geq functions are designed to be as lightweight and efficient as possible, and they load asynchronously to minimize impact on your site’s performance.
Q: What if my website uses a single-page application framework?
For single-page applications (SPAs) built with frameworks like React, Angular, or Vue.js, the geq.page function should be called whenever the view changes instead of just once when the page loads. This typically means triggering the function within the routing logic of your application.
Q: What are product attributes, and why are they important for Reclaim events?
A: Product attributes are details about the products on your website, like name, price, ID, and category. These details are sent to the Reclaim product through tracking events to identify which products users are interacting with. They're essential for your marketing content - emails and SMS.
Q: How can I ensure that the product attributes are dynamically set?
A: Dynamically setting product attributes means that the values change based on the product the user is viewing or interacting with. You can achieve this by using a template system provided by your e-commerce platform, which automatically inserts the correct product details into the JavaScript code, or by rendering these details on the server-side before the page is sent to the user's browser.
Q: Can you give me an example of how to use a template to set these attributes?
A: Most e-commerce platforms have a templating language. For example, Shopify looks like this:
var item = {
"Name": "{{ product.title }}",
"Price": {{ product.price | money_without_currency }},
"ProductID": "{{ product.id }}",
"Categories": {{ product.categories | json }},
"ImageURL": "{{ product.image.src }}",
"URL": "{{ shop.url }}{{ product.url }}",
"Brand": "{{ product.vendor }}"
};
This code uses Shopify's Liquid templating language to insert the current product's details into the item object, which can then be used with the geq.event/geq.addToCart function.
Q: What if my e-commerce platform doesn't provide a templating system?
A: If your platform doesn't provide a templating system, or if you're rendering your pages on the server (server-side rendering), you will need to generate the product attributes in your server-side code and embed them into the page's HTML before it's sent to the browser.
Q: How do I handle optional attributes like 'Brand' or 'Categories'?
A: For optional attributes, you should check if the data exists before adding it to the object. If it doesn't exist, you can either omit the attribute from the object or set it to a default value that indicates it's not applicable.
Q: How do I test to make sure the attributes are set correctly?
A: After setting up your product attributes, you should test them by visiting the relevant pages on your site and checking the browser's console for the correct values. Additionally, you can use tools provided by your Reclaim dashboard to verify that the data is being received as expected.
Q: What are common mistakes to avoid when setting up product attributes?
A: Common mistakes include not matching the attribute names exactly to what Reclaim expects, missing out on dynamic values so that all products send the same data, and syntax errors in the code due to incorrect template usage. Always double-check your code, and test thoroughly.
When integrating "Reclaim" products into your website, it's essential to accurately set product attributes. This ensures that the data sent through geq.event calls are precise and meaningful. Most e-commerce platforms provide a templating system that you can utilize to dynamically assign these values. Here's how you can do it for some common platforms:
Please note that we have our own private Shopify app that will automatically add our products to your site - no action is required for Shopify customers!
var item = {
"Name": "{{ product.title }}",
"Price": {{ product.price | money_without_currency }},
"ProductID": "{{ product.id }}",
"Categories": {{ product.categories | json }},
"ImageURL": "{{ product.image.src }}",
"URL": "{{ shop.url }}{{ product.url }}",
"Brand": "{{ product.vendor }}"
};
Magento uses PHP on the backend, but you can pass variables to the frontend via JSON:
<script type="text/javascript">
var item = {
"Name": "<?php echo $block->escapeJs($block->escapeHtml($_product->getName())) ?>",
"Price": <?php echo json_encode($_product->getFinalPrice()) ?>,
"ProductID": "<?php echo $_product->getId() ?>",
"Categories": <?php echo json_encode($_product->getCategoryIds()) ?>,
"ImageURL": "<?php echo $block->getImageUrl($_product, 'product_page_image_large') ?>",
"URL": "<?php echo $_product->getProductUrl() ?>",
"Brand": "<?php echo $_product->getAttributeText('manufacturer') ?>"
};
</script>
For WooCommerce on WordPress, you would typically use PHP to handle data on the server side. Please see our dedicated guide at:
For BigCommerce on WordPress, you would typically use PHP to handle data on the server side. Please see our dedicated guides at:
In Adobe Experience Manager (AEM), you can use the Satellite object _satellite.getVar to retrieve dynamic variables.The _satellite.getVar method requires you to have set up data elements within Adobe Experience Platform Launch to return the appropriate values.
var item = {
"Name": _satellite.getVar('ProductName'),
"Price": _satellite.getVar('ProductPrice'),
"ProductID": _satellite.getVar('ProductID'),
"Categories": _satellite.getVar('ProductCategories'), // Assumes this is a JSON array
"ImageURL": _satellite.getVar('ProductImageURL'),
"URL": _satellite.getVar('ProductURL'),
"Brand": _satellite.getVar('ProductBrand')
}
NOTE: This may need to be adjusted based on your setup and is only intended as a guideline.
Fetch Product DetailsUses digital data; for more information, please refer to: https://experienceleague.adobe.com/docs/analytics/implementation/vars/page-vars/products.html?lang=en
//Fetch product details via data element
var item = {};
var prod = digitalData.products;
if (prod.length > 0) {
item.Name = prod[0].productName;
item.ProductID = prod[0].productID;
item.ImageURL = prod[0].productImageURL;
item.URL = window.location.href;
item.Price = prod[0].productPrice;
item.index = 0;
}return item;
Viewed Product
<script type="text/javascript">
var item = _satellite.getVar('klaviyo_items');
//Ensure we have the required attributes (can be modifed)
function isValidItem(item ) { return item !== undefined && item.hasOwnProperty('ImageURL') && item.ImageURL !== null && item.ImageURL !== undefined && item .hasOwnProperty('Price') && item.Price !== null && item.Price !== undefined;}
if (isValidItem(item)) {
geq.event('Viewed Product Reclaim', item);
}
</script>
Add to Cart
//Add to cart script - requires the Fetch Product Details Script above
const primaryButtons = document.getElementsByClassName("btn btnPrimary");
const secondaryButtons = document.getElementsByClassName("btn btnSecondaryOne addToCartOrderBtn");
function isValidItem(atcItem) {
return atcItem !== undefined &&
atcItem.hasOwnProperty('ImageURL') && atcItem.ImageURL !== null && atcItem.ImageURL !== undefined &&
atcItem.hasOwnProperty('Price') && atcItem.Price !== null && atcItem.Price !== undefined;
}
function addToCartHandler(atcItem) {
if (isValidItem(atcItem)) {
geq.addToCart(atcItem);
}
}
function handleButtonClick() {
var atcItem = _satellite.getVar('klaviyo_items');
addToCartHandler(atcItem);
}
for (const button of primaryButtons) {
button.addEventListener("click", handleButtonClick);
}
for (const button of secondaryButtons) {
button.addEventListener("click", handleButtonClick);
}