> ## 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 **Native Klaviyo integration** preview enabled for your project
* Permission to connect your Klaviyo account to Firmhouse
* Access to **Customer Portal > Templates** so you can edit Liquid templates

Ask Firmhouse to enable **Native Klaviyo integration** for your project if you do not see the OAuth connection option in the Klaviyo app settings.

## Connect Klaviyo with OAuth

1. In Firmhouse, open **Apps > Klaviyo**.
2. Click **Connect Klaviyo**.
3. Sign in to Klaviyo and approve the requested access.
4. Return to Firmhouse and confirm that the Klaviyo settings show **OAuth connected**.

Firmhouse requests access to send events, read profiles and segments, and keep customer profile properties current. OAuth tokens are stored encrypted and refreshed automatically. You do not need to create or paste a private API key.

If Firmhouse shows **New permissions required**, click **Reconnect Klaviyo** and approve the current permissions.

Legacy public API keys can still send existing Klaviyo metric events, but they cannot read profile properties or segment membership. Connect with OAuth to use Klaviyo data in Customer Portal templates.

Read Klaviyo's [OAuth overview](https://developers.klaviyo.com/en/docs/set_up_oauth) for more information about how Klaviyo authorizes connected applications.

## 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 Klaviyo is not connected with OAuth, the subscription has no email address, or 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 OAuth access or refresh tokens 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 OAuth connection option is not visible**

Ask Firmhouse to enable **Native Klaviyo integration** for your project. While this feature is in preview, the connection option is only shown for selected projects.

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

Check that Klaviyo shows **OAuth connected** in Firmhouse and that the current subscription email address exists as a profile in Klaviyo. Reconnect Klaviyo if Firmhouse reports that new permissions are required.

**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)
