Solution Recipe 11: Use webhooks in flows to create more fine-grained segments

Saul
9 min read
For developers
September 26, 2022

Solution Recipes are tutorials to achieve specific objectives in Klaviyo. They can also help you master Klaviyo, learn new third-party technologies, and come up with creative ideas. They are written mainly for developers & technically-advanced users.

Note: We do our best to make sure any code and API references are accurate and current when this is published, but you might need to update code and it’s always a best practice to leverage our latest API versions. If you have questions, feel free to hop over to our Developer Community.

What you’ll learn

Klaviyo has powerful built-in segmentation capabilities, where you can target profiles based on their properties and on actions (metrics) they have taken, and can further fine-tune the segmentation to properties of those metrics. This article will build on Solution Recipe 10: Use webhooks in flows to send additional event and profile data into Klaviyo, to permit more fine-grained segmentation based on actions profiles have taken.

One common use case is to target high-spending customers, specifically customers who have placed orders above a certain value threshold in a given time period. Using the built-in Klaviyo tools, you can segment profiles by the total value of their orders in a time period, but not based on the value of individual orders. For example, suppose we want to segment profiles who have placed one order of at least $1,000, versus profiles who have placed 50 orders of $20 apiece (for a total of $1,000).

Using a combination of Klaviyo flows, webhooks and template tagging, we can store dynamically-updated profile properties whose values we can use to extend Klaviyo segmentation.

Why it matters

Klaviyo’s segmentation capabilities are among its most powerful features, and being able to further segment profiles based on the actions they have taken allows you to take full advantage of Klaviyo.

Level of sophistication

Moderate

Prerequisite

Read Solution Recipe 10: Use webhooks in flows to send additional event and profile data into Klaviyo to learn how Klaviyo flow webhooks work, and how you can use them to call the Klaviyo track and identify APIs to set profile properties and add custom metrics.

Because this recipe uses webhook flow actions, you will need to enable two-step authentication on your Klaviyo account.

Introduction

In our example, we have a basic Placed Order metric (created with the Track Profile Activity API), that contains a timestamp, an array of ordered items, and a value.

{
    "created_at": "2022-09-22T20:41:17.298858",
    "items": [
        {
            "ProductName": "Brooklyn Bridge Canvas Print",
            "Size": "small"
        },
        {
            "ProductName": "Brooklyn Bridge Canvas Print",
            "Size": "medium"
        },
        {
            "ProductName": "Brooklyn Bridge Canvas Print",
            "Size": "small"
        }
    ],
    "$value": 1359
}

We have two goals:

  • build a segment of all profiles who have placed an individual order of at least a certain value within the past 30 days
  • build a segment of all profiles who have ordered an item whose size (photo print size in this case) is a certain value (e.g. “medium” or “large”) in the past 30 days

We will build a flow that is triggered off the Placed Order event, and which uses webhooks with the Identify Profile API to save profile properties that come from the event data, e.g. the order’s value and the size property of ordered items.

Instructions

Step 1: Build the flow

Here is how the flow will look when completed. It is triggered off the “Placed Order” event that our e-commerce integration sends to Klaviyo when customers place orders.

We are going to store two profile properties, one whose value is the date of the highest value order the profile placed within the past 30 days, and a second property whose value is the monetary value of that order.

We first check whether the profile has placed any other orders in the past 30 days. If they have not, then we know that this current order is by definition the highest value order in the past 30 days, and we do not need to do any comparison of the current order’s value with the profile properties we are storing. Because the current order that triggered the flow is included in the conditional split count, we set the condition to “has placed at most one order in the past 30 days”.

In this case, we add the following webhook action.

The destination URL of the webhook is Klaviyo’s Identify API — https://a.klaviyo.com/api/identify

The webhook JSON body is as follows:

{
    "token": "[public key]",
    "properties": {
      "$email": "{{ person.email }}",
      "highest_order_value": {{ event|lookup:'$value'|default:0 }},
      "highest_order_date": "{{ event.created_at }}"
    }
}

Replace [public key] with your Klaviyo account’s 6-character public key.

You may need to change {{ event.created_at }} to use whatever event property stores the date of the current order. You can also use the Django 1 now tag.

Suppose the following Placed Order event triggered the flow:

If this is the profile’s first order in 30 days, the following profile properties will be saved:

If the profile has placed at least one other order in the past 30 days, then we need to compare the value of the current order against the value of 1 highest_order_value stored in the profile, and only update the profile property if the current order’s value is higher.

Here is the webhook action JSON body for the “No” branch of the conditional split, meaning the profile has placed at least one other order in the past 30 days. The URL remains https://a.klaviyo.com/api/identify

{% with profile_order_value=person|lookup:'highest_order_value'|floatformat:"0" %}
    {% with order_value=event|lookup:'$value'|floatformat:"0" %}
        {% if profile_order_value < order_value %}
            {
                "token": "[public key]",
                "properties": {
                  "$email": "{{ person.email }}",
                  "highest_order_value": {{ order_value }},
                  "highest_order_date": "{{ event.created_at }}"
                }
            }        
        {% else %}
            {
                "token": "[public key]",
                "properties": {
                  "$email": "{{ person.email }}"
                }
            }
        {% endif %}        
    {% endwith %}
{% endwith %}

We only update highest_order_value and highest_order_date if the value stored in the profile for the highest recent order value is less than the value of the current order. Otherwise, we would ideally want to cancel the webhook call, but since we cannot do that, we send a “dummy” webhook call that does not update any new profile properties.

Step 2: Build the segment

Once the flow is live and the profile properties start being added, we can create a segment of profiles who have placed an order of at least a certain value in the past 30 days:

We want to look for all profiles whose highest_order_date is within 30 days, and whose highest_order_value is at least our threshold. As new profiles place orders above this threshold, they will be added to this segment, and as profiles who had placed such orders at one point but no longer are, they will be removed from the segment.

If we want to find profiles who have ever placed such a high-valued order, regardless of when, we can remove the first condition of the segment definition.

Step 3 (optional): Save ordered item properties to profiles

Our example Placed Order data includes details about the ordered items, in this case, a photo size:

{
    "ProductName": "Brooklyn Bridge Canvas Print",
    "Size": "small"
}

Suppose we want to segment on profiles who have ordered photos of certain sizes. With the native segmentation features, we can segment on top-level properties of event data. With this solution recipe, we can save profile properties whose values are nested non-top level values of the event data.

Add a webhook action to the flow, with the same URL of https://a.klaviyo.com/api/identify, with the following JSON body. It should be outside the “has placed an order in the past 30 days” conditional split, because we do not have to worry about comparing numeric values of orders like we did with the original webhook.

{
    "token": "[public key]",
    "properties": {
      "$email": "{{ person.email }}",
      "small_order_date": "{% for item in event.items %}{% if item.Size == "small" %}{{ event.created_at }}{% endif %}{% endfor %}",
      "medium_order_date": "{% for item in event.items %}{% if item.Size == "medium" %}{{ event.created_at }}{% endif %}{% endfor %}",
      "large_order_date": "{% for item in event.items %}{% if item.Size == "large" %}{{ event.created_at }}{% endif %}{% endfor %}",
      "xlarge_order_date": "{% for item in event.items %}{% if item.Size == "xlarge" %}{{ event.created_at }}{% endif %}{% endfor %}"
    }
}

For each possible value of the print size, we save a profile property such as medium_order_date, and to determine its value, we loop through each of the items of the order and check whether the item’s Size property is the value in question. If so, we set the value to the order’s date, so the profile property reflects the date of the most recent order that had an item meeting the corresponding condition.

(Note again that you will need to use the actual event data property names for your particular e-commerce platform.)

Here are example profile properties that will be saved from this webhook:

Step 4 (optional): Build segments for additional order properties

Suppose we want to find all profiles who have placed an order with a medium photo print within the past 30 days. Here is the segment we would add:

Enhancements and limitations

Further date-based segmentation

We could add conditional splits for other date ranges beyond 30 days, e.g. 60 and 90 days, and save profile properties such as 60_day_highest_order_value and 60_day_highest_order_date. We can then build segments based on profiles who have placed orders of certain values within these varying date ranges.

Historical orders

Because we need the flow to save the profile properties as orders are placed, we will not be able to start building segments until the flow is live and orders start coming into Klaviyo. Orders placed before the flow was enabled, or orders imported via a historical CSV upload, will not trigger the flow, and therefore won’t cause the profile properties to be set.

If you have an offline process that can take historical orders and use the same logic as the flow to generate the values of the profile properties that the flow sets, you could do a bulk CSV update of profiles with earlier placed orders, using the same profile property names the flow updates. Your segments will then include profiles who placed orders before the flow was created and turned on.

Learn more!

If you’re interested in learning more about Klaviyo’s developer experience and APIs, please visit developers.klaviyo.com!

Saul
Saul Blumenthal

Related content

For developers
Dec 22, 2023
Account Subscription History Solution Recipe

This Solution Recipe goes over how to extract historical data regarding a profile’s subscription timeline.

For developers
Dec 4, 2023
Solution Recipe: Using AI and APIs to create and upload images to your Klaviyo account

Solution Recipes are tutorials to achieve specific objectives in Klaviyo. They can also help you master Klaviyo, learn new third-party technologies, and come up with creative ideas. They are written mainly for developers and technically-advanced users. Note: We do our best to make sure any code and API references are accurate and current when this […]

For developers
Oct 24, 2023
Solution Recipe: Append, unappend, and unset custom properties programmatically with Klaviyo

Solution Recipes are tutorials to achieve specific objectives in Klaviyo. They can also help you master Klaviyo, learn new third-party technologies, and come up with creative ideas. They are written mainly for developers and technically-advanced users. Note: We do our best to make sure any code and API references are accurate and current when this […]