HomeGuidesAPI Reference
ChangelogHelp CenterCommunityContact Us
Guides
These docs are for v2023-10-15. Click to read the latest docs for v2024-10-15.

Custom integration FAQs

Common questions and answers when building a custom integration with Klaviyo.

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 metrics in our standard action-word-first naming scheme include:

  • Viewed Product
  • Started Checkout
  • Placed Order

📘

There is a difference between Metric and Event endpoints. An event is the time-stamped action tied to a specific profile. Metrics are the rolled-up concepts of those events. For example, Placed Order is a reportable metric and is backed by individual Placed Order events. In other words, "Metric" is the name of an action. An "Event" is a time-stamped occurrence of that action by a profile.

Placed Order metric activity feed in Klaviyo showing list of 3 timestamped Placed Order events

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

Many templating languages have a markup that use 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 notations 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 and log in on your site, you can cookie them so that their onsite activity (e.g. Viewed Product) can be tracked in their Klaviyo profile. Klaviyo does not track events for anonymous sessions that we haven’t cookied, so you will want to set that cookie as soon as you have identifying properties — e.g. email address or phone number — for the profile.

Suppose you have a customer object that stores the email address and name of the customer who just logged in or created an account. You can make the following JavaScript call to cookie the session and tie it to the corresponding Klaviyo profile:

klaviyo.identify({
  "$email" : customer.email,
  "$first_name" : customer.first_name,
  "$last_name" : customer.last_name
}).then(() => console.log("Identify has been completed"));

You can also send other properties about the logged in user with this Identify request, if they are available to your code. See our Profile properties reference for a full list of Klaviyo properties.

📘

For more information on the Klaviyo JavaScript object, read our introduction to the Klaviyo object.

How do I enable personalized recommendations?

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

  • A catalog feed, synced via a native integration, custom catalog feed, or the Catalogs API.
  • A metric you want to base recommendations on; we recommend Ordered Product, but this may differ 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 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 a 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 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).

See the example below for the ItemNames and Items arrays containing detailed information about each product.

{
    "data": {
        "type": "event",
        "id": "4jsX38gkQ2e",
        "attributes": {
            "timestamp": 1685976596,
            "event_properties": {
                "OrderId": "1234",
                "Categories": [
                    "Fiction",
                    "Classics",
                    "Children"
                ],
                "ItemNames": [
                    "Winnie the Pooh",
                    "A Tale of Two Cities"
                ],
                "Brands": [
                    "Kids Books",
                    "Harcourt Classics"
                ],
                "DiscountCode": "Free Shipping",
                "DiscountValue": 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"
                    }
                ],
                "BillingAddress": {
                    "FirstName": "Matt",
                    "LastName": "Kemp (Sample)",
                    "Company": "",
                    "Address1": "125 Summer St",
                    "Address2": "6th floor",
                    "City": "Boston",
                    "Region": "Massachusetts",
                    "RegionCode": "MA",
                    "Country": "USA",
                    "CountryCode": "US",
                    "Zip": "02111",
                    "Phone": "5005550006"
                },
                "ShippingAddress": {
                    "FirstName": "Matt",
                    "LastName": "Kemp (Sample)",
                    "Company": "",
                    "Address1": "125 Summer St",
                    "Address2": "6th floor",
                    "City": "Boston",
                    "Region": "Massachusetts",
                    "RegionCode": "MA",
                    "Country": "USA",
                    "CountryCode": "US",
                    "Zip": "02111",
                    "Phone": "5005550006"
                },
                "$event_id": "sample_data_gen:5f5c645d-5260-43e1-a10b-d07f48f2157f",
                "$value": 29.98
            },
            "datetime": "2023-06-05 14:49:56+00:00",
            "uuid": "3c8c0200-03b0-11ee-8001-49f53f64f7ce"
        },
        "relationships": {
            "profile": {
                "data": {
                    "type": "profile",
                    "id": "01H260JEDNJQ6CFZM2MVWF9950"
                },
                "links": {
                    "self": "https://a.klaviyo.com/api/events/4jsX38gkQ2e/relationships/profile/",
                    "related": "https://a.klaviyo.com/api/events/4jsX38gkQ2e/profile/"
                }
            },
            "metric": {
                "data": {
                    "type": "metric",
                    "id": "S4SFhi"
                },
                "links": {
                    "self": "https://a.klaviyo.com/api/events/4jsX38gkQ2e/relationships/metric/",
                    "related": "https://a.klaviyo.com/api/events/4jsX38gkQ2e/metric/"
                }
            }
        },
        "links": {
            "self": "https://a.klaviyo.com/api/events/4jsX38gkQ2e/"
        }
    }
}

This is how the event data will appear within Klaviyo:

Placed Order event details showing various top level properties

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.
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.
Make sure to send data you want to use in segmentation as top-level properties in the payload.

Should I use email/phone or external ID as my unique identifier?

The Create Profile and Create or Update Client Profile endpoints enable you to identify and define properties for an individual. You can identify a profile by:

  • Their email address (email), or if they're an SMS-only customer, their phone number (phone_number).
  • A unique identifier, such as the external ID (external_id).

Profile fields can be nulled out by passing null into a profile field via the Update Profile endpoint.

We recommend using email for your main identifier, if available. You can also use phone number, but you need to have SMS enabled to set phone_number with our APIs, which validate the phone number against your account's region. 

📘

The external_id property should not be used to track profiles anonymously, because this could create multiple profiles for individuals in your account. If you choose to use a unique identifier via the external_id key, you will be responsible for identity management within your Klaviyo account. You must ensure every call you make maps the correct external ID to the associated email.

For example, calls sent with only an external 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.

The external_id property has a limit of 64 characters. Any characters over the limit will be truncated.

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. You can create a test checkout and test order on your site to send data back to Klaviyo. Then, you can check whether Klaviyo is receiving events correctly.

Log in to your Klaviyo account and navigate to Analytics > Metrics to see all of the different events (or metrics) syncing into your Klaviyo account.

Metrics tab in Klaviyo showing list of Metrics filtered by APIt

Select an individual metric, then click on the Activity Feed 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 or private API key you're using match the API key in your account? Make sure you replaced the placeholder API key in any example snippets with your own.
  • For JavaScript requests: are profiles identified before you make the event tracking request? Klaviyo does not track anonymous activity.

How do I backfill historical data?

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

Backfill data via API

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

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

Backfill data via CSV

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

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

Backfill data via SFTP

You can bulk import CSV profile and event data via Klaviyo’s SFTP tool with any SFTP client. Operations currently supported include:

  • Event: create
  • Profiles: create, update

You’ll need to:

  1. Generate a public/private key pair on your local machine.
  2. Initiate a SFTP import with a correctly formatted CSV.
  3. Check your ingest job status via in-app messages.

When backfilling data via SFTP, note that:

  • Klaviyo will not ingest files with any row (including headers) exceeding 1MB.
  • Event CSVs and profile CSVs are formatted differently, and certain fields are required for data creation to occur.
  • Klaviyo will attempt to ingest duplicate files.
  • Because profiles and events are unique, the same CSV ingested twice will only perform a single create or update operation.
  • Ingest jobs will complete prior to metrics and profiles being available in your account.

Can I use a tag manager to integrate?

Yes. Our Create or Update Client Profile, Create Client Event, and Create Client Subscription requests can be sent via JavaScript, and thus injected via a tag manager, but here are a few things to keep in mind:

  • Our requests may not trigger correctly for people who have:

    • Poor internet connection.
    • Slow computers.
    • Ads or JS blockers installed on their browsers.
  • Information stored either in a client-side 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?

To create events, profiles, or subscriptions from server-based applications, use the Create Event, Create Profile, and Subscribe Profile endpoints respectively. 

To create a new event, profile, or subscription, or to track a profile’s activity from publicly-browseable, client-side environments, use the Create Client Event, Create or Update Client Profile, and Create Client Subscription endpoints. On the client side, you can also use the Klaviyo JavaScript object, which offers a shorthand way to interact with our APIs and send events.

See the table below for some advantages and disadvantages of each method.

Client-sideServer-side
Advantages
  • Can be implemented on any platform that supports JavaScript.
  • Does not require access to the server-side 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 client-side.
  • Supports more private requests.
Disadvantages
  • 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 server side of a platform or to its APIs.

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

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

  • Viewed Product
    When a user visits a product page.
  • Added to Cart
    When a user adds an item to their cart.
  • Started CheckoutGenerally 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 pre-built 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 an “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 an error response

There are a number of common payload mistakes that may cause errors.

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.

Arguments representing string literals are expressed with quotation marks (we will accept either single-quoted or double-quoted strings). Single or double-quoted characters within strings (quoted with like quote characters) must be escaped with a single backslash (i.e. Tony\’s ball). Comparisons to all string literals are case-sensitive, unless otherwise noted in endpoint-specific documentation.

Valid email address

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

Formatting

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 it can cause the JSON to be invalid in some languages/validators.
You can also use a JSON validator such as https://jsonlint.com, which shows any JSON formatting errors. Learn more about JSON filtering syntax.

I received a "1" response from a client call, but my event isn't appearing in Klaviyo

Client calls are asynchronous and either return a 201 success code with a "1" in the body, or an error code if the API call fails. A 201 success code does not necessarily mean that the call has been processed successfully, just that it has been received by Klaviyo.

If you are receiving an unexpected response or the data is failing to populate in your Klaviyo account once the info has asynchronously processed, here are a few things that might be happening:

  • Every event for a given profile must have a unique combination of event name (event) and an event ID. If you follow 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 should receive an in-app and email notification if the limit is hit.

Read more about error handling and rate limits.

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 coupon upload feature.