All Collections
Other Integrations
Custom Website Installation Guide
Custom Website Installation Guide
Updated over a week ago

1. Introduction

Welcome to the Retention Implementation Guide for your custom or headless 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

Purpose of the Guide

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.

Product Definitions

  • Grow: This product helps in identifying anonymous visitors on your website, enabling you to engage them with introductory email campaigns.

  • Reclaim: This product focuses on recognizing users who are browsing your site and showing an intent to purchase, allowing you to retarget them with abandonment email campaigns.


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

  • Access to Website Backend: You need to have access to the backend or content management system (CMS) of your website to insert JavaScript code.

  • Basic JavaScript and HTML Knowledge: A basic understanding of JavaScript and HTML is required. You will be working with a JavaScript snippet and potentially need to make minor adjustments to fit your website’s specific tech stack.

  • Unique Company ID: A unique identifier for your company is provided by your implementation manager. This ID is crucial for the correct functioning of the "Grow" and "Reclaim" products.

2. Implementing the Retention Code Snippet

The code snippet is the backbone of’s product functionality. It's essential that this snippet is present on every page of your website where our functions will be used.

Location of the Snippet in HTML

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.

Where do I get my Snippet?

  1. Go to your dashboard. From the navigation menu on the left, click on "Code Script"

  2. Click on "View your Script" on the top right hand of the page.

  3. Select the appropriate script you want to install. Then click "Copy Code".


  • Insert Into the <head> Tag: In your website's HTML, find the <head> tag. If you are using a template or a content management system, this will typically be in a global header file that's included on every page.

  • Ensure Global Presence: To reiterate, it is crucial that the code snippet is included on every page of your website where "Grow" and "Reclaim" product functions are expected to operate. This is typically achieved by placing the snippet in a template file that is shared across all web pages.

Verify Implementation:

  1. After adding the snippet to your website, it's important to verify that it's present and correctly configured. You can do this by viewing the page source of your live site or using browser developer tools.

  2. If you are using a CDN or other advanced setups, you may not see this inside of your website. In your browser, open the console, and check your network section for ‘ge.js’.

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:

  • Load your website and use the developer console in your browser to check for any errors or warnings that may indicate a problem with the snippet.

  • Navigate through different pages to make sure the snippet is loaded each time.

3. Implementing The Grow Product Features

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.

Single Function Call on Every Page

NOTE: If you included the <script> </script> in the code snippet function above, you can skip this step.

The `` 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 `` function call.

<script type="text/javascript">
// ...Your Retention code snippet...;

(OPTIONAL - Advanced) Include An Identifier with Your Contacts:

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 
// Then call the page function;

Revenue Tracking (Order Tracking) Script on Order Completion Page

The order tracking script is used to track the success of Grow by reporting conversions back to our system. We automatically duplicate 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">
  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

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.

Suppress Function for Voluntary Email Submission

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: Attentive, 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

Grow Testing

Test all of the functions you’ve included!

  • Page Tracking: Navigate through your site and verify that the `` function is being called on every page without errors.

  • Order Tracking: Conduct a test purchase to ensure that the order tracking is capturing and sending data accurately.

  • Email Submission: Test the email submission points to ensure that the `geq.suppress` function executes as expected.

You may encounter a 403 error for the "ge.js" file. This is normal and will go away once your implementation manager sets your scripts to live!

4. Implementing Reclaim Product Features

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.

Template Placeholders

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.

Add to Cart Event

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.

  1. Locate the script or trigger that runs when a product is added to the cart.

  2. Here, implement the geq.addToCart function to fire with the product's 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') {


  • Ensure all product details are dynamically populated based on the user's interaction.

  • Only ProductID is required. If you do not have one, a placeholder can be used.

  • The variables can be re-labelled to match the payload expected by the platforms this data will be sent to.

Viewing a Product Event

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.

  1. Find the code that runs when a product page is loaded.

  2. Add the geq.event function call within the product page code to execute when the page is viewed.

<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);}


  • Ensure all product details are dynamically populated based on the user's interaction.

  • Only ProductID is required. If you do not have one, a placeholder can be used.

  • The variables can be re-labelled to match the payload expected by the platforms this data will be sent to.

(Optional) Viewed Category Event

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

Klaviyo Users Quick Guide: Easily Install Reclaim

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.

Integrating Reclaim with Your Klaviyo Set-Up

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

Klaviyo X Viewed Product

// 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": "##{{ }}",
 "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);
{% endif %}

Klaviyo X Add To Cart

//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 
// Method 2: (ADVANCED) Customize the item
// NOTE: you can edit the payload if you need to
   if (typeof geq !== 'undefined' && typeof geq.addToCart === 'function') {
       name: item.ProductName,
       price: item.Price,
       productID: item.ProductID,
       categories: item.Categories,
       imageURL: item.ImageURL,
       URL: item.URL,
       brand: item.Brand,
CompareAtPrice: item.CompareAtPrice});}

Klaviyo X Checkout Started

<script type="text/javascript">
//Create the object for the Klayvio Started Checkout event which will look something like:
var checkoutData = {
     "$event_id": "1000123_1387299423",
     "$value": 29.98,
     "ItemNames": ["Winnie the Pooh", "A Tale of Two Cities"],
     "CheckoutURL": "",
     "Categories": ["Fiction", "Children", "Classics"],
     "Items" = [{
        "ProductID": "1111",
        "SKU": "WINNIEPOOH",
        "ProductName": "Winnie the Pooh",
        "Quantity": 1,
        "ItemPrice": 9.99,
        "RowTotal": 9.99,
        "ProductURL": "",
        "ImageURL": "",
        "ProductCategories": ["Fiction", "Children"]
        "ProductID": "1112",
        "SKU": "TALEOFTWO",
        "ProductName": "A Tale of Two Cities",
        "Quantity": 1,
        "ItemPrice": 19.99,
        "RowTotal": 19.99,
        "ProductURL": "",
        "ImageURL": "",
        "ProductCategories": ["Fiction", "Classics"]
// Call to Klayvio's Started Checkout event using the object from above
_learnq.push(["track", "Started Checkout", checkoutData ]);
// Call to Retention's Checkout Started Reclaim event using the same object
geq.event("Checkout Started Reclaim", checkoutData)

(OPTIONAL - Advanced) Custom Event Tracking

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

5. Frequently Asked Questions (FAQs)

Code Snippet

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

  1. After installing the code snippet and any geq function calls, you should test your website to ensure everything is tracking correctly.

  2. Adding ?vge=true to the url of your website brings up the R! Debugging tool, which will show you which of our functions are found and running on your site.

    1. Please see a detailed guide here:

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

RECLAIM: How do I correctly set up the product attributes for the Reclaim tracking events?

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": "##{{ }}",
  "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.

Platform-Specific Templates for Setting Product Attributes

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": "##{{ }}",
  "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') ?>"

WooCommerce (WordPress)

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:

Adobe Experience Manager

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

Adobe Experience Manager With Klaviyo

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:

//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);

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)) {
function handleButtonClick() {
  var atcItem = _satellite.getVar('klaviyo_items');
for (const button of primaryButtons) {
  button.addEventListener("click", handleButtonClick);
for (const button of secondaryButtons) {
  button.addEventListener("click", handleButtonClick);
Did this answer your question?