> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firmhouse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrate data from Klaviyo

> Use Klaviyo profile properties and segment membership inside Customer Portal v2 Liquid templates.

Customer Portal v2 templates can read Klaviyo profile properties and segment membership for the customer that is logged in. Use this when you want to personalize the portal based on data you already maintain in Klaviyo.

For example, you can:

* Show a VIP message to customers in a `VIP members` segment
* Show a product recommendation when a Klaviyo profile property has a certain value
* Hide or show a portal action based on campaign, loyalty, churn-risk, or lifecycle data
* Pair a personalized Customer Portal message with a Klaviyo metric that tracks when a customer acts on it

## Before you start

You need:

* Customer Portal v2 enabled for your project
* The Klaviyo app enabled in Firmhouse
* A Klaviyo public API key for existing Klaviyo metric events
* A Klaviyo private API key for reading profile properties and segment membership
* Access to **Customer Portal > Templates** so you can edit Liquid templates

The private API key is used server-side by Firmhouse. Do not place a private Klaviyo API key in Customer Portal template JavaScript, custom HTML, or browser-visible code.

While this feature is in preview, ask Firmhouse to enable **Klaviyo Profile Liquid** for your project if you do not see the private API key field in the Klaviyo app settings.

## Create a private API key in Klaviyo

You must have an Owner, Admin, or Manager role in Klaviyo to manage API keys.

1. In Klaviyo, open **Settings > API keys**.
2. Under **Private API Keys**, click **Create Private API Key**.
3. Give the key a recognizable name, such as `Firmhouse Customer Portal`.
4. Select **Custom Key**.
5. Give the key these read permissions:
   * `profiles:read`
   * `segments:read`
6. Create the key and copy it immediately. Klaviyo only shows a private API key once.

Klaviyo does not let you edit scopes on an existing private API key. If you created a key without the required permissions, delete that key and create a new one with `profiles:read` and `segments:read`.

Read Klaviyo's [Authenticate API requests](https://developers.klaviyo.com/en/docs/authenticate_) guide for the latest details about private API keys and scopes.

## Set up Klaviyo profile access

1. In Firmhouse, open **Apps > Klaviyo**.
2. Enable the Klaviyo app if it is not enabled yet.
3. Enter your **Public API Key**.
4. Enter your **Private API Key**.
5. Save the settings.

When a private API key is already configured, Firmhouse shows that the key is set. Leave the private key field blank when saving if you want to keep the current key. Paste a new private key only when you want to replace it.

The public key and private key serve different purposes:

* The **public API key** is used for Klaviyo metric tracking and client-side Klaviyo usage.
* The **private API key** is used by Firmhouse to read Klaviyo profiles and segments on the server.

## Use the Klaviyo profile Liquid object

Klaviyo data is available through the current subscription:

```liquid theme={null}
{{ subscription.klaviyo_profile }}
```

The `klaviyo_profile` object exposes:

| Property        | Description                                                        |
| --------------- | ------------------------------------------------------------------ |
| `properties`    | The Klaviyo profile properties for the subscription email address. |
| `segments`      | A list of segment objects with `id` and `name`.                    |
| `segment_ids`   | A list of Klaviyo segment IDs.                                     |
| `segment_names` | A list of Klaviyo segment names.                                   |

Firmhouse looks up the Klaviyo profile by the subscription email address. If no private API key is configured, if the subscription has no email address, or if Klaviyo does not return a profile, the object renders as empty data.

## Show content based on a profile property

Use `subscription.klaviyo_profile.properties` when the decision is based on a custom property on the Klaviyo profile.

```liquid theme={null}
{% assign klaviyo_properties = subscription.klaviyo_profile.properties %}

{% if klaviyo_properties.customer_tier == "vip" %}
  <div class="rounded-lg border border-emerald-200 bg-emerald-50 p-4">
    <p class="font-semibold text-emerald-950">Thanks for being a VIP customer.</p>
    <p class="mt-1 text-sm text-emerald-900">
      Here is an extra recommendation for your next order.
    </p>
  </div>
{% endif %}
```

Klaviyo property keys can be accessed with dot notation when the key is Liquid-friendly, such as `customer_tier`. For keys with spaces or special characters, use bracket notation or normalize the property name in Klaviyo first.

```liquid theme={null}
{{ subscription.klaviyo_profile.properties["Customer Tier"] }}
```

## Show content based on segment membership

Use `segment_names` when you want to check whether a customer is in a named Klaviyo segment.

```liquid theme={null}
{% assign klaviyo_segment_names = subscription.klaviyo_profile.segment_names %}

{% if klaviyo_segment_names contains "VIP members" %}
  <div class="rounded-lg border border-emerald-200 bg-emerald-50 p-4">
    <p class="font-semibold text-emerald-950">VIP members</p>
    <p class="mt-1 text-sm text-emerald-900">
      You are part of the VIP members segment in Klaviyo.
    </p>
  </div>
{% endif %}
```

Use `segment_ids` instead of `segment_names` when you want the template logic to keep working if a segment is renamed in Klaviyo.

```liquid theme={null}
{% assign klaviyo_segment_ids = subscription.klaviyo_profile.segment_ids %}

{% if klaviyo_segment_ids contains "SEGMENT_ID_FROM_KLAVIYO" %}
  <p>This message is shown for a specific Klaviyo segment.</p>
{% endif %}
```

## Combine Klaviyo data with Customer Portal actions

You can combine Klaviyo conditions with Customer Portal components. For example, show an add-product action only for customers in a specific segment:

```liquid theme={null}
{% assign klaviyo_segment_names = subscription.klaviyo_profile.segment_names %}

{% if klaviyo_segment_names contains "VIP members" %}
  <div class="rounded-lg border border-gray-200 bg-white p-4">
    <p class="font-semibold text-gray-900">Add a VIP product to your next order</p>
    <p class="mt-1 text-sm text-gray-600">
      Choose an extra item and add it to your next shipment.
    </p>

    <div class="mt-4">
      {% add_product size: "small" %}
    </div>
  </div>
{% endif %}
```

## Track Customer Portal actions in Klaviyo

Use Klaviyo's public Client Event API when you want to track that a Customer Portal message was shown or clicked. This is a browser-side API, so use your Klaviyo public API key, also called the Site ID. Never expose your Klaviyo private API key in Customer Portal HTML or JavaScript.

A practical pattern is to define one helper function in the `shared_head` template, and then call that helper from any page, dialog, or button where you want to track a Customer Portal action.

```html theme={null}
<script>
  window.trackKlaviyoCustomerPortalEvent = function(metricName, options = {}) {
    const publicApiKey = "PUBLIC_API_KEY_OR_SITE_ID";
    const email = options.email;
    const properties = options.properties || {};

    if (!publicApiKey || !email || !metricName) return Promise.resolve();

    return fetch(`https://a.klaviyo.com/client/events?company_id=${encodeURIComponent(publicApiKey)}`, {
      method: "POST",
      headers: {
        "Content-Type": "application/vnd.api+json",
        "revision": "2026-04-15"
      },
      body: JSON.stringify({
        data: {
          type: "event",
          attributes: {
            metric: {
              data: {
                type: "metric",
                attributes: {
                  name: metricName
                }
              }
            },
            profile: {
              data: {
                type: "profile",
                attributes: {
                  email: email
                }
              }
            },
            properties: properties
          }
        }
      })
    }).catch(function(error) {
      console.warn("Could not track Klaviyo event", error);
    });
  };
</script>
```

After that helper is available, call it from the template where the event happens. For example, track that a VIP dialog was shown:

```liquid theme={null}
{% if klaviyo_segment_names contains "VIP members" %}
  <script>
    window.trackKlaviyoCustomerPortalEvent("Viewed VIP Customer Portal offer", {
      email: "{{ subscription.email | escape }}",
      properties: {
        subscription_id: "{{ subscription.id }}",
        segment: "VIP members",
        location: "dashboard"
      }
    });
  </script>
{% endif %}
```

Or track a button click:

```liquid theme={null}
<button
  type="button"
  onclick="window.trackKlaviyoCustomerPortalEvent('Clicked VIP add product CTA', {
    email: '{{ subscription.email | escape }}',
    properties: {
      subscription_id: '{{ subscription.id }}',
      segment: 'VIP members',
      location: 'dashboard'
    }
  })"
>
  Add to my next order
</button>
```

Klaviyo accepts the event request asynchronously. Check the profile or metric in Klaviyo to confirm the event was received.

## Caching and freshness

Firmhouse caches Klaviyo profile properties and segment membership for 10 minutes. This keeps Customer Portal pages fast, because Firmhouse does not have to request data from Klaviyo on every page load.

This means changes to Klaviyo profile properties or segments can take up to 10 minutes to show up in the Customer Portal.

When you are developing Customer Portal templates in preview mode, Klaviyo caching is disabled. Preview mode always requests the latest profile properties and segment membership from Klaviyo so you can test template changes faster.

When you update Klaviyo properties that influence segment membership, open the segment in Klaviyo and click **Update segment**. This makes sure the segment reflects the latest matching profiles before you test the Customer Portal.

## Troubleshooting

**The private API key field is not visible**

Ask Firmhouse to enable **Klaviyo Profile Liquid** for your project. While this feature is in preview, the field is only shown for selected projects.

**The template does not show my Klaviyo content**

Check that the Klaviyo app is enabled, the private API key is configured, and the current subscription email address exists as a profile in Klaviyo.

**A segment change in Klaviyo is not visible immediately**

Klaviyo segment membership can take time to update in Klaviyo. If you changed profile properties that affect segment membership, open the segment in Klaviyo and click **Update segment**. Outside Customer Portal template preview mode, Firmhouse can take up to 10 minutes to show the latest Klaviyo data.

**The Customer Portal page still loads when Klaviyo is unavailable**

This is expected. Firmhouse treats Klaviyo profile data as optional personalization data. When the API cannot be reached or returns an error, the Liquid object returns empty properties and segment lists.

## Related articles

* [Customer Portal quickstart](/customer-portal-v2/quickstart)
* [Building templates](/customer-portal-v2/components/overview)
* [Integrating Shopify data](/customer-portal-v2/shopify-storefront-and-metafields)
* [Send Firmhouse emails through Klaviyo](/configure/customer-communication/using-klaviyo)
