Integrate a Custom Ecommerce Cart or Platform

Overview

Before reading through this article, make sure you're not using an ecommerce platform or cart already supported by Klaviyo. Klaviyo integrates with over a dozen ecommerce platforms out-of-the-box. If Klaviyo has a built-in integration for your ecommerce platform, you can skip these instructions because we've done the heavy lifting for you!

If you've built your own ecommerce solution or are using a platform we don't support yet, there are three types of data you need to send to Klaviyo:

  • Website Activity: When people visit your website and which products people are viewing
  • Orders & Order Items: Purchase events and what items are in those purchases
  • Checkouts Started: When visitors start the checkout process (used to trigger Abandoned Cart emails)

The sections below walk through how to start sending each type of data to Klaviyo for your custom cart.

Keep in mind that the detailed data you send to Klaviyo along with website, purchase, and checkout events will determine how you can filter and segment these events in Klaviyo. To understand how data must be structured so that key event details are available for segmentation, check out this guide

Website Activity

To track website activity, first find your public API key by logging into your account and going to Account > Settings > API Keys. Your public key is six characters long. Add the following snippet of code to your main store template so it's included on all pages. You should place this snippet either with other analytics scripts you use or right before the closing </body> tag.

Make sure to replace API_KEY with your Klaviyo account's Public API key:

<script>
  var _learnq = _learnq || [];
  _learnq.push(['account', 'API_KEY']);
  (function () {
    var b = document.createElement('script'); b.type = 'text/javascript'; b.async = true;
    b.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'a.klaviyo.com/media/js/analytics/analytics.js';
    var a = document.getElementsByTagName('script')[0]; a.parentNode.insertBefore(b, a);
  })();
</script>

If visitors or customers can create accounts for your store, add the following snippet directly below the first snippet:

<script>
  var _learnq = _learnq || [];
  {% if user.is_logged_in %}
  _learnq.push(['identify', {
    $email: '{{ user.email }}',
    $first_name: '{{ user.first_name }}',
    $last_name: '{{ user.last_name }}', 
  }]);
  {% endif %}
</script>

Depending on the types of templates you use for your website, the {% if user.is_logged_in %} and {{ user.email }} syntax are likely different. Using the template language available, you want to check if the person viewing the current page is logged in. If so, you should output their email and name, if available. If you don't have name information, remove those two lines and the trailing comma after the email $email line.

Viewed Product

If you'd like to set up a Browse Abandonment flow or build segments based on product browsing data, you'll want to add JavaScript event tracking for a "Viewed Product" metric. On your product page template, add the following snippet:

<script text="text/javascript">
var _learnq = _learnq || [];
var item = {
Name: '{{ product.title }}',
ProductID: {{ product.id }},
Categories: {{ category in product.categories|json }}, // The list of categories is an array of strings.
ImageURL: '{{ product.image_url }}',
URL: '{{ product.url }}',
Brand: '{{ product.brand }}',
Price: {{ product.price }},
CompareAtPrice: {{ product.compare_at_price }} // If you have a compare at price. You could also include this for a sale or special price.
}
};

_learnq.push(['track', 'Viewed Product', item]);

_learnq.push(['trackViewedItem', {
Title: item.Name,
ItemId: item.ProductID,
Categories: item.Categories,
ImageUrl: item.ImageURL,
Url: item.URL,
Metadata: {
Brand: item.Brand,
Price: item.Price,
CompareAtPrice: item.CompareAtPrice
}
}]);
</script>

The snippet above uses the {{ }} placeholder syntax which may need to be altered for your WooCommerce platform. The important part is that product fields are dynamically rendered based on which product page you're viewing.

When you add Klaviyo web tracking to your site, we are only able to track the browsing activity of "known browsers" - i.e. browsers that have visited and engaged at least once before. There are two key ways we are able to identify a site visitor for web tracking purposes:

  • If someone has, at some point, clicked through a Klaviyo email to your website
  • If someone has, at some point, subscribed/opted-in through a Klaviyo form

Klaviyo will not track anonymous browsers. After Viewed Product web tracking has been configured for your site, "Viewed Product" data should begin populating in your Klaviyo account as known visitors browse your product pages.

Orders & Order Items

To track orders and order details, after an order is placed, you should make a track call to our Server-Side API. The API requires making an HTTP request with a JSON and base64 encoded payload. Check to see if you can use one of Klaviyo's language libraries, which will automatically handle encoding your API calls. Otherwise, use the built-in libraries for the language you're using to encode your requests.

You'll want to send order data to Klaviyo in one of two ways: real-time or in batch.

  • Real-time: You'll make requests as soon as an order is placed
  • Batch: You should write a script that will run at least once an hour that makes one track request for each order in the last time period
NOTE: It's important you send this data at least once an hour if you'll be sending abandoned cart emails 2-4 hours after someone starts checking out. If you'll be sending them even sooner, e.g. 30 minutes after someone starts checking out, you'll need to send order data in real-time or at least every 20 minutes.

For each order, we recommend you send two types of events: one event called "Placed Order" for the entire order and one event for each line item called "Ordered Product." 

As an example, if you were running a store that sold books and John Smith placed an order for Winnie the Pooh and a Charles Dickens' Tale of Two Cities, you would make the below track for "Placed Order:"

{
  "token" : "API_KEY",
  "event" : "Placed Order",
  "customer_properties" : {
    "$email" : "john.smith@example.com",
    "$first_name" : "John",
    "$last_name" : "Smith"
  },
  "properties" : {
    "$event_id" : "1234",    
    "$value" : 29.98,
    "Categories" : ["Fiction", "Classics", "Children"],
    "ItemNames" : ["Winnie the Pooh", "A Tale of Two Cities"],
    "Brands" : ["Kids Books", "Harcourt Classics"],
    "Discount Code": "Free Shipping", // Discount code (if applicable)
    "Discount Value": 5, // Value of discount (if applicable)
    "Items" : [
      {
        "SKU" : "WINNIEPOOH",
        "Name" : "Winnie the Pooh",
        "Quantity" : 1,
        "ItemPrice" : 9.99,
        "RowTotal" : 9.99,
        "ProductURL" : "http://www.example.com/path/to/product",
        "ImageURL" : "http://www.example.com/path/to/product/image.png",
        "Categories" : ["Fiction", "Children"],
        "Brand" : "Kids Books"
      },
      {
        "SKU" : "TALEOFTWO",
        "Name" : "A Tale of Two Cities",
        "Quantity" : 1,
        "ItemPrice" : 19.99,
        "RowTotal" : 19.99,
        "ProductURL" : "http://www.example.com/path/to/product2",
        "ImageURL" : "http://www.example.com/path/to/product/image2.png",
        "Categories" : ["Fiction", "Classics"],
        "Brand" : "Harcourt Classics"
      }
   ]
  },
  "time" : 1387302423
}
Here are a few notes on the above JSON payload:
  • Make sure to replace API_KEY with your public API key from https://www.klaviyo.com/integrations
  • $event_id is a special property which uniquely identifies this event in case you send it to Klaviyo twice; the value should be the order ID from your database
  • $value is a special property that allows Klaviyo to track revenue; this should be the total price of the order
  • The Items array should contain one dictionary for each line item; if the quantity for a line item is greater than one, the Row Total should be the Item Price multiplied by the quantity
  • Product URL and Image URL are optional, but we recommend them in case you want to send post-purchase emails highlighting previous purchases
    • For example, if you wanted to send a "Please write us a review" follow-up, displaying a picture of the products purchased and linking directly to them makes it more likely for your customers to follow through
  • The time should be a UNIX timestamp of the order date and time
  • The brand and categories fields are optional (if you don't have them).

Then for each line item, you should make track call for an "Ordered Product" event, as well:

{
  "token" : "API_KEY",
  "event" : "Ordered Product",
  "customer_properties" : {
    "$email" : "john.smith@example.com",
    "$first_name" : "John",
    "$last_name" : "Smith"
  },  
  "properties" : {
    "$event_id" : "1234_WINNIEPOOH",
    "$value" : 9.99,
    "Name" : "Winnie the Pooh",
    "Quantity" : 1,
    "ProductURL" : "http://www.example.com/path/to/product",
    "ImageURL" : "http://www.example.com/path/to/product/image.png",
    "ProductCategories" : ["Fiction", "Children"],
    "ProductBrand" : "Kids Books"
  },
  "time" : 1387302423
}
A few notes about the "Ordered Product" calls:
  • Note there are two calls, one for each line item. Also note that most of the data is the same as theItems array from the "Placed Order" event. This is on purpose.
  • $event_id in this case should be a unique identifier for the line item for that order. It's common to do that by combining the order ID and the SKU or product ID together. In this case we combined them with an underscore. If you have an order item ID for each line item, you could use that as well.
  • $value is the Row Total from the previous track call and is the total price of that line item.
  • Product Categories is very important piece of data if you have it. This will allow you to find people who have purchased products from specific categories. This is very useful when you have new products and want to target campaigns to people who might be interested in similar products. We recommend that Product Categories represents the entire category hierarchy for that product so you can find customers who've purchased from narrow as well as broad categories.
  • If you have Brand or other attributes about products you might want to use for targeting later, include those as additional key-value pairs in the properties dictionary. In our example, it might make sense to include an Author property so we can let customers know about new books from that author or recommend other books by the same author in the future.

At this point, you're tracking the most important order data. If your cart also tracks when orders are fulfilled or shipped, we recommend also sending a "Fulfilled Order" event. The track call for that event should look the same as the "Ordered Product" event except the timestamp should be when the order was fulfilled or shipped. This event is useful for doing post-purchase follow-ups which are more coordinated with when someone receives their package. For example, you can send a "How are you liking your PRODUCT?" email two weeks after someone's order has been fulfilled if you know all your orders ship in 7-10 days.

Here's an example track call for a "Fulfilled Order" event:

{
  "token" : "API_KEY",
  "event" : "Fulfilled Order",
  "customer_properties" : {
    "$email" : "john.smith@example.com",
    "$first_name" : "John",
    "$last_name" : "Smith"
  },
  "properties" : {
    "$event_id" : "1234",
    "$value" : 29.98,
    "Items" : [
      {
        "SKU" : "WINNIEPOOH",
        "Name" : "Winnie the Pooh",
        "Quantity" : 1,
        "ItemPrice" : 9.99,
        "RowTotal" : 9.99,
        "ProductURL" : "http://www.example.com/path/to/product",
        "ImageURL" : "http://www.example.com/path/to/product/image.png",
        "ProductCategories" : ["Fiction", "Children"]
      },
      {
        "SKU" : "TALEOFTWO",
        "Name" : "A Tale of Two Cities",
        "Quantity" : 1,
        "ItemPrice" : 19.99,
        "RowTotal" : 19.99,
        "ProductURL" : "http://www.example.com/path/to/product2",
        "ImageURL" : "http://www.example.com/path/to/product/image2.png",
        "ProductCategories" : ["Fiction", "Classics"]
      }
    ]
  },
  "time" : 1387302453
}

Note the only differences are the event and time values.

Checkouts Started

Checkout data is important if you'd like to send abandoned cart emails. When someone starts the checkout process, you'll send Klaviyo an event indicating he/she started checking out. The best place to trigger this event is when someone visits the checkout page and they've "identified" themselves by either logging in or entering an email address.

To start, let's assume someone is logged in. In that case, you'll have already called identify as part of the website activity tracking we set up above, so all you need to do is make a track call like the one below.

The "Checkout Started" event looks very similar to the "Placed Order" event above, except that the event is different. You'll want to make sure you include all the details of the line items so your abandoned cart emails can be customized to include pictures and links to all products in someone's cart. It's probably easiest to format the JSON data in the checkout page template where you can access to the cart contents, but other ways of getting the data will work as well. Here's an example track call using our previous order example:

_learnq.push(['track', 'Started Checkout', {
  "$event_id" : "1000123", // The cart ID if you have it. Otherwise remove this line.
  "$value" : 29.98,
  "ItemNames" : ["Winnie the Pooh", "A Tale of Two Cities"],
  "Checkout URL": "http://www.example.com/path/to/checkout",
  "Items" : [
    {
      "SKU" : "WINNIEPOOH",
      "Name" : "Winnie the Pooh",
      "Quantity" : 1,
      "ItemPrice" : 9.99,
      "RowTotal" : 9.99,
      "ProductURL" : "http://www.example.com/path/to/product",
      "ImageURL" : "http://www.example.com/path/to/product/image.png",
      "ProductCategories" : ["Fiction", "Children"]
    },
    {
      "SKU" : "TALEOFTWO",
      "Name" : "A Tale of Two Cities",
      "Quantity" : 1,
      "ItemPrice" : 19.99,
      "RowTotal" : 19.99,
      "ProductURL" : "http://www.example.com/path/to/product2",
      "ImageURL" : "http://www.example.com/path/to/product/image2.png",
      "ProductCategories" : ["Fiction", "Classics"]
    }
  ]
}]);
A few notes on this "Checkout Started" track call:
  • $event_id should be a unique identifier for the cart if you have one, typically the cart or quote ID stored in your database.
  • If you want to include a description of a product, include a Description key-value in each line item dictionary.
  • If someone comes to the checkout page and is still anonymous, which is the most probable situation, you'll need to wait for them to enter their email address to trigger the event above.
  • If your checkout is spread across multiple pages, you can add the above call on the page in the checkout after they've entered their contact information, most importantly their email address
  • If your checkout is on a single page, you might need to monitor the email field for changes.  
    • For example, you could use jQuery and an event listener like so send the "Checkout Started" event only after someone enters their email:
<script>
  var _learnq = _learnq || [];

  // Helper function that wraps the big "track" call above.
  var trackStartedCheckout = function () {
    _learnq.push(['track', 'Started Checkout', {
      // Data omitted for brevity, but this dictionary would match what's in the call above.
    }]);
  };

  // Note, this assumes a <form id="payment_form"> and an field <input name="email" type="email" value="" />
  $('#payment_form [name="email"]').change(function (e) {
    var email = $(this).val();
    // Do some light validation. Klaviyo will do more validation when the data is received.
    if (email && /@/.test(email)) {
      _learnq.push(['identify', { $email: email }]);
      trackStartedCheckout();
    }
  });
</script>

Check that your Custom Integration is Working

The first thing to check is whether or not Klaviyo is receiving events correctly. To do this, you can log into your Klaviyo account and click on the Metrics tab. This tab will show all the different events (or metrics) syncing into your Klaviyo account. You should see the following metrics here: "Placed Order," "Ordered Product," and "Started Checkout." You may also see a metric for "Active on Site." If you do, that means we've received at least one event for each of those metrics.

Next, for each metric, click on the Activity Feed icon to view a list of the most recent events we've received. If you don't see the metrics you're expecting in Klaviyo, here are a few things to consider:

  • The "Placed Order" and "Checkout Started" metrics won't show up until someone has placed an order or checked out, respectfully
    • You should create a test checkout and a test order to see data appear
  • Check that the public API key you're making API calls with matches the API in your account
    • Sometimes people forget to replace the placeholder API key
  • For JavaScript track calls, make sure people are being identified before you make the track call
    • Klaviyo doesn't track anonymous activity because you can't take action or send emails to people who haven't been identified

If none of these appear to be the issue, send us an email and we'll help you troubleshoot!

Was this article helpful?
1 out of 1 found this helpful