HomeGuidesAPI Reference
ChangelogHelp CenterCommunityContact Us
These docs are for v1-2. Click to read the latest docs for v2024-02-15.

Custom integration FAQs

If you’re building a custom integration to Klaviyo using our APIs, here are some common questions and answers to keep in mind.

How should I name my metrics?

We recommend naming your metrics using action words. Since metrics are triggered when someone takes an action, the name should reflect that action. Examples of events in our standard action-word-first naming scheme include:

  • Viewed Product
  • Started Checkout
  • Placed Order

Why do your code examples have so many curly brackets {{ }}?

Many templating languages have a markup that uses curly brackets {{ }} to retrieve server-side information when the page loads. The actual language and words used in the examples, as well as the curly bracket notation itself, may need to be altered for your platform. Values for each property using the curly bracket markup are then dynamically rendered based on the information needed for that property.

My customers can create accounts; how do I cookie them?

If customers can create accounts for your store, you can include an if statement in the template language to check if the customer is logged in:

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

If the customer is logged in, output their email and name (if available). If you don’t have their email and name, remove the references to first name and last name.

You can also send other properties about the logged in user with this Identify request, including $first_name and $last_name (if those are available through your templating language or data layer). See our Getting started with properties for a full list of Klaviyo properties.

How do I enable personalized recommendations?

Setting up personalized recommendations based on your catalog requires three items:

  • A catalog feed.
  • A metric you want to base recommendations on; we recommend Ordered Product, but this may be different depending on your business.
  • A top-level property on that metric which represents the ID for a given product. This must be a string or number (not an array), and must match the type and value of the product ID ($id) field for a product in your catalog.

See our guide on how to Sync a custom catalog feed to Klaviyo to learn how to configure your feed.

How should I structure my data for segmentation and flow filtering?

If you’re building your own custom integration, you’ll need to determine the following:

  • Which data to send to Klaviyo
  • How to structure the data

Make sure all of the properties you want to segment on are sent as top-level properties (i.e., not nested).

For example, if you create a Placed Order metric to track when someone completes a purchase on your site, you may want to filter events based on what item(s) someone purchased.

To do this, create two top-level arrays in properties:

  • ItemNames: include all of the purchased product names.
  • Items: populate with nested arrays to include all of the associated product details for each product in ItemNames. This nested data is helpful later on when iterating over the items in the order, so you can display properties about each item one at a time (e.g., ProductName, ItemPrice, and Quantity).

The ItemNames array is then sent via the Placed Order payload.

See the example of an ItemNames array below, along with the nested Items array containing detailed information about each product:

   "token": "API_KEY",
   "event": "Placed Order",
   "customer_properties": {
     "$email": "[email protected]",
     "$first_name": "John",
     "$last_name": "Smith",
     "$phone_number": "5551234567",
     "$address1": "123 Abc st",
     "$address2": "Suite 1",
     "$city": "Boston",
     "$zip": "02110",
     "$region": "MA",
     "$country": "USA"
   "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 Value": 5,
     "Items": [{
         "ProductID": "1111",
         "SKU": "WINNIEPOOH",
         "ProductName": "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"
         "ProductID": "1112",
         "SKU": "TALEOFTWO",
         "ProductName": "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

Why aren’t certain values appearing in my segment and flow filters?

Klaviyo's segment and flow filters populate with metric or profile property values that have previously been sent to your account. Until we receive a metric or profile property with that data, it won’t appear in the drop-down for filtering.

Make sure to choose different names for your metric properties and profile properties. If a metric property shares a name with a profile property, the metric will not appear in the segment builder or flow filter options.

Should I use $email or $id as my unique identifier?

The Identify method, which allows you to identify and set properties on an individual, uses one of the following methods to identify a person:

  • Their email address ($email), or if they're an SMS-only customer, their phone number ($phone_number)
  • A unique identifier, such as their user ID ($id)

We highly recommend that you consistently identify all individuals using their email address ($email) only, and do not pass any $id values.

If you choose to use a unique identifier via the $id key, you will be responsible for identity management within your Klaviyo account. You must ensure every call you make maps the correct $id to the associated $email. Calls sent with only an $id value will cause Klaviyo to create profiles without an associated $email. If a call then comes in for the same person, but only identifies them via their $email, a permanent duplicate profile will be created for the same person.

As of April 2022, the $id property has a limit of 64 characters. Any characters over the limit will be truncated.


The $id key should never be used to track profiles anonymously, as this has a high potential of creating multiple profiles for individuals in your account if not set up thoughtfully. Only use $id if a given person has a known $email and their associated ID will never change (e.g., an account ID in your own system).

How do I tell if my custom integration is working?

Certain metrics won’t appear in Klaviyo until there’s data to show. For example, the Placed Order and Started Checkout metrics won’t appear in Klaviyo until someone has either placed an order or started a check out, respectively. Therefore, first create a test checkout and test order in your site to send test data back to Klaviyo. Then you can check whether Klaviyo is receiving events correctly.

Log into your Klaviyo account, and go to Analytics > Metrics to see all of the different events (or metrics) syncing into your Klaviyo account. You should see the following:

  • Placed Order
  • Ordered Product
  • Started Checkout
  • Active on Site (optional)

If you do, that means we've received at least one event for each of those metrics.

Click on the Activity Feed for each metric to view a list of the most recent events Klaviyo has received.

If you don't see the metrics you're expecting in Klaviyo, check the following settings:

  • Does the public API key you're making requests with match the API key in your account?
    • Did you replace the placeholder API key with your own?
  • For JavaScript Track requests: are people identified before you make the Track request?
    • Klaviyo doesn't track anonymous activity because you can't take action or send emails to people who haven't been identified

How do I backfill historical data?

Did your store or website exist before your integration with Klaviyo? If so, there are two ways to bring in all of your historical data to Klaviyo: backfilling via API or CSV.

Backfilling via API

If you have historical data on your customers, you can send that to Klaviyo using our Track and Identify APIs. If you’ve already built an integration to Klaviyo for real-time data, the process here is very similar.

To send historical data to Klaviyo, you’ll need to iterate through every historical event you have and send a Track request for that event using the timestamp of when it occurred. If you have historical profile properties (e.g., customer’s email preferences, birthdays, etc.), you can send that information back to Klaviyo using our Identify API.

Backfilling via CSV

This historical information can also be sent via CSV uploads. Here are a few considerations to keep in mind when using this method:

  • A CSV upload must be less than 50 MB.
  • Event CSVs and contact CSVs are formatted differently and uploaded in separate areas of the platform.
  • Each event CSV upload must contain headers with the name of the property on the event you’re uploading.
  • Event CSVs can only contain flat structures, where each row represents a singular event for a given metric; nested data structures cannot be uploaded via CSV (only top-level properties). For example, you cannot make a CSV that mimics the data structure of our recommended Placed Order event, but you can make a CSV that mimics the data structure of our recommended Ordered Product event.

Can I use a tag manager to integrate?

Yes. Any of our Track or Identify requests can be sent via JavaScript, thus injected via a tag manager, but here are a few things necessary to keep in mind:

  • Our requests may not trigger correctly for people who have:
    • Poor internet connection
    • Slow computers
    • Ad or JS blockers installed in their browsers
  • Information stored either in a front-end data layer or in the JavaScript of a website may be visible to the public internet; don’t send or expose any sensitive information about your browsers or customers when integrating using only Javascript

Should I use the server-side or front-end API?

Our Track and Identify APIs have both server-side and front-end (JavaScript) components. Both the server-side and front-end requests can be used to send the same data, but there may be certain instances where one is more useful than the other. See the table below for some advantages and disadvantages of each method.

  • Can be implemented on any platform that supports JavaScript
  • Does not require access to the back-end code of a platform, so it can be used on hosted solutions (e.g., Shopify and BigCommerce)
  • Relatively straight-forward to set up
  • Runs on your own server
  • Highly reliable
  • Access to information only available on the server (e.g., order status) or information passed back to the server from the front-end
  • Supports more private requests
  • Runs on the end-user’s computer
  • Can be interrupted by JavaScript blockers, slow internet connections, or slow computers
  • Exposed to the public internet
  • Requires access to either the back-end of a platform or to its APIs

What’s the difference between abandoned carts and abandoned checkouts?

When interacting on the frontend, a user creates three metrics in Klaviyo when they’re shopping on a site:

  • Viewed Product: when a user clicks into an item
  • Add to Cart: when a user adds an item to their cart
  • Started Checkout: generally triggered either when a user lands on the checkout page or when they add their email to the checkout form (dependent on platform)

Klaviyo has multiple default flows in the Flows Library called “abandoned cart,” which use either the Added to Cart or the Started Checkout metric as a flow trigger. Other platforms may refer to the latter flow as “abandoned checkout” instead of “abandoned cart,” since the flow is triggered when someone lands on the checkout page. Both of the triggers refer to a cart that was abandoned somewhere in the customer journey, and therefore can both be used to kick off an “abandoned cart” flow. In addition to these two flows, browse abandonment flows (triggered by the Viewed Product metric) target site visitors who browsed without adding items to their cart.

My payload looks valid, but I'm receiving a "0" response?

Our Track and Identify APIs will respond with a "0" if the payload sent was invalid. Here are some reasons you may be getting an unexpected response or unexpected results:

Payload encoding

If you are using our GET endpoint, the payload of the request is a URL parameter so it will also need to be URL encoded. Make sure the payload is first base64 encoded then URL encoded. Some languages group these encodings into a single function and some do not, and a payload that is only base64 encoded may still be invalid as a URL parameter.

Quotation marks

There may be curly or fancy quote marks used in your payload instead of regular quote marks.

  • ' and " are valid quote marks
  • ', ', ", and " may be added by some text-editing softwares, but are not valid programming quote marks and will cause the JSON to be invalid if used outside of a string

Valid email address

A server-side payload must contain a valid email address as the $email property in its customer_properties dictionary.


All non-final elements in lists and JSON should end with a comma, but make sure that all final elements in both the lists and JSON objects do not end with a comma. While this formatting is technically valid in some languages, it’s safer to not include it as a best practice, as it can cause the JSON to be invalid in some languages/validators.

I received a "1" response from the track API, but my event isn't appearing in Klaviyo?

If the sent payload was valid, our Track and Identify APIs will respond with a "1". However, this does not indicate that the event itself was saved for a given profile on the account. See the following reasons you may receive a "1" response, but the event does not appear in Klaviyo:

  • Every event tracked to your account for a given profile must have a unique combination of event name (event) and event ID ($event_id). If you track the same combination a second time to the same profile, you will receive a "1" response, but the event will not be ingested.
  • @example.com domain email addresses are technically valid, but we do not accept them into our event or profile processing pipeline.
  • By default, an account is limited to 200 unique metrics, or the number of overall unique "names" or types of events (e.g., Placed Order). You can still send as many unique events (single occurrences of a metric like individual Placed Orders) as you like. You should receive an in-app and email notification if the limit is hit, and any further net-new metric creation via the Track API will respond with a "1" but not generate that metric on the account.

How do I set up coupons?

If you are not using one of our pre-built integrations that include coupon functionality, you can set up coupons by using our bulk coupon upload feature.