Connect Klaviyo with Shopify

Implementation Guide

Overview: Connecting Klaviyo and Shopify

Klaviyo and Shopify represent the two most operationally critical platforms in a modern direct-to-consumer e-commerce stack. Klaviyo functions as the behavioral intelligence layer, aggregating email engagement events, flow progression states, predictive analytics outputs, and custom metric data into rich customer profiles. Shopify serves as the transactional backbone, managing orders, customer records, inventory, discount logic, and storefront rendering. While Shopify's native Klaviyo integration—installed via the Klaviyo app in the Shopify App Store—handles the standard synchronization of order events, cart activity, and customer records from Shopify into Klaviyo, the reverse direction is where most e-commerce teams hit the ceiling: using Klaviyo's behavioral signals to actively modify Shopify customer records, trigger Shopify Flows, or generate discount codes requires custom integration work that the native connector does not support.

This guide covers the reverse integration: pushing Klaviyo behavioral events and profile state changes downstream into Shopify to close the feedback loop between marketing intelligence and storefront execution. The archetypal use case is post-flow action execution. When a Klaviyo flow reaches a terminal "converted" branch, the ideal response is to update the corresponding Shopify customer record with a tag, metafield, or loyalty status change that influences future storefront behavior—serving a personalized experience, suppressing irrelevant pop-ups, or unlocking access to a restricted product collection. This level of storefront personalization is impossible without a live bidirectional data channel, and it is the capability that distinguishes mature e-commerce operations from those still treating Klaviyo as a one-way broadcast channel.

Core Prerequisites

On the Klaviyo side, you need an account on the Growth, Pro, or Enterprise plan. Flow-level webhooks and the Profiles API write endpoints are not available on the free tier. From Klaviyo's account settings, generate a private API key and assign the following permissions: Read/Write on Profiles (for reading profile data and writing custom properties), Read/Write on Events (for recording custom metric events), Read on Flows (for querying flow state), and Read on Campaigns (for campaign engagement data retrieval). For inbound event push from Klaviyo to your middleware, navigate to Integrations > Custom Integrations > Webhooks and configure a webhook endpoint subscribed to the specific metric events (such as "Opened Email," "Completed Flow," or any custom event you've defined) that should trigger downstream Shopify actions.

On the Shopify side, create a Custom App in your store's Admin under Settings > Apps and Sales Channels > Develop Apps. Generate API credentials with the following Admin API access scopes: write_customers and read_customers for tag and metafield operations, write_discounts and read_discounts for programmatic Price Rule and Discount Code management, write_orders if you need to annotate order records with note attributes, and read_price_rules for verifying existing rule configurations before creating codes. The Admin API access token is displayed exactly once upon credential generation—store it immediately in your secrets manager. The base URL format for Shopify's Admin REST API is https://{your-store}.myshopify.com/admin/api/2024-04/{endpoint}.json. For complex mutations such as bulk metafield updates, the GraphQL Admin API at https://{your-store}.myshopify.com/admin/api/2024-04/graphql.json is the more efficient choice.

If deploying this integration via Make, pre-authorize both Klaviyo and Shopify connections in your organization's connection manager. Make's native Klaviyo modules support profile retrieval and list operations; for webhook-driven flows originating from Klaviyo's metric events, use a Custom Webhook module as the entry point and HTTP modules for Shopify REST API calls where Make's native Shopify modules lack the required granularity (particularly for ContentDocumentLink-equivalent operations in the Shopify data model).

Top Enterprise Use Cases

The first enterprise use case is Shopify customer tagging based on Klaviyo flow completion. When a profile exits a re-engagement flow via the "converted" branch—triggered by a custom "Completed Flow" metric event that you configure in the Klaviyo flow's exit action—the integration appends a tag such as klaviyo-reengaged-q3-2024 to the corresponding Shopify customer record. This tag can subsequently be used to create a Shopify customer segment for targeted promotions, as a suppression condition in future Klaviyo flows (so recently re-engaged customers aren't immediately entered into another re-engagement sequence), or as a filter condition in Shopify Flow automations that trigger loyalty reward assignments.

A second high-value use case is dynamic, single-use discount code generation and delivery. When a profile reaches a specific step in a Klaviyo browse abandonment or cart recovery flow, the integration calls Shopify's Price Rules API to generate a unique discount code scoped to that customer's email address, then writes the generated code back to the Klaviyo profile as a custom property. The subsequent email in the Klaviyo flow template renders that property as a dynamic variable, ensuring each customer receives a genuinely unique, non-transferable offer. This is architecturally superior to batch-generating discount codes in Shopify and uploading them as a static list to Klaviyo, because it eliminates code exhaustion risk and allows discount validity windows to be set relative to the moment of code generation rather than the moment of upload.

A third use case is storefront personalization via Shopify customer metafields populated from Klaviyo predictive data. Klaviyo's predictive analytics generates engagement decile scores, predicted lifetime value estimates, and churn risk probabilities for each profile. The integration runs as a nightly scheduled batch job that reads these predictive properties from Klaviyo's Profiles API and writes them into Shopify customer metafields (for example, namespace: klaviyo, key: predicted_ltv, value: 2450.00, type: number_decimal). Shopify storefront themes and Liquid templates can then read these metafields using customer.metafields.klaviyo.predicted_ltv to surface personalized product recommendations, adjust promotional offer intensity, or route customers to different checkout experiences based on their predicted value tier.

Step-by-Step Implementation Guide

The integration architecture has two distinct data flows: an event-driven push from Klaviyo to Shopify triggered by metric events, and a scheduled batch sync that periodically reads Klaviyo profile properties and writes them as Shopify customer metafields. This guide covers the event-driven flow first because it is the more architecturally complex path, and then addresses the batch metafield synchronization pattern.

For the event-driven flow, configure a Klaviyo Webhook to POST events to your middleware. From Klaviyo's Integrations > Custom Integrations > Webhooks panel, create a new webhook, select the metric events that should trigger downstream Shopify actions (such as "Placed Order," a custom "Completed Flow" metric, or "Subscribed to List"), and enter your middleware's HTTPS callback URL. Klaviyo signs webhook payloads using an HMAC-SHA256 signature included in the X-Klaviyo-Signature header, which your middleware should validate using your Klaviyo private API key. The webhook payload for a metric event uses the Klaviyo Data Object format:

{
  "data": {
    "type": "event",
    "id": "event-id-abc123",
    "attributes": {
      "metric_id": "METRICXYZ",
      "profile_id": "01GDDKASDR8CM6W20ET4BMWX7X",
      "datetime": "2024-09-15T10:30:00+00:00",
      "event_properties": {
        "flow_id": "FLOWID123",
        "branch_outcome": "converted"
      }
    }
  }
}

Upon receiving this payload, extract the profile_id and call GET https://a.klaviyo.com/api/profiles/{profile_id}/ with headers Authorization: Klaviyo-API-Key {your_private_key} and revision: 2024-02-15. The response contains the profile's email attribute, which is your bridge to the Shopify customer record. Look up the Shopify customer by calling GET https://{your-store}.myshopify.com/admin/api/2024-04/customers/search.json?query=email:{email_address}&fields=id,tags,email with the X-Shopify-Access-Token: {your_access_token} header. Parse the returned customer id and existing tags string.

To add a new tag without overwriting existing ones, split the tags string on ", ", append your new tag to the resulting array, deduplicate, then rejoin with ", " as the separator. Send a PUT request to https://{your-store}.myshopify.com/admin/api/2024-04/customers/{customer_id}.json with the merged tags value in the body:

{
  "customer": {
    "id": 6789012345678,
    "tags": "existing-tag-one, existing-tag-two, klaviyo-reengaged-q3-2024"
  }
}

For the discount code generation workflow, first create a Price Rule via POST https://{your-store}.myshopify.com/admin/api/2024-04/price_rules.json. The Price Rule body defines the discount's mechanics: value_type set to percentage or fixed_amount, value as a negative number representing the discount magnitude, customer_selection set to prerequisite with a prerequisite customer ID set to restrict the discount to the specific customer, usage_limit set to 1 for single-use enforcement, and ends_at set to a timestamp 72 hours from the current time for urgency. Once the Price Rule is created and its id is returned, generate the actual code via POST https://{your-store}.myshopify.com/admin/api/2024-04/price_rules/{price_rule_id}/discount_codes.json with an empty or auto-generated code value in the body. The returned code string is then written back to the Klaviyo profile using PATCH https://a.klaviyo.com/api/profiles/{profile_id}/ with a body setting attributes.properties.discount_code to the generated code value, making it available in subsequent flow email templates as {{ person.discount_code }}.

In a Make scenario, the event-driven tag update flow is: Module 1 (Custom Webhook receiving Klaviyo's POST), Module 2 (HTTP GET to Klaviyo Profiles API for email retrieval), Module 3 (HTTP GET to Shopify Customer Search API), Module 4 (Tools "Set Variable" module using Make's split, add, deduplicate, and join functions to reconstruct the tags string safely), Module 5 (HTTP PUT to Shopify Customers API). For the discount code flow, insert Modules 6 and 7 after Module 3: Module 6 is an HTTP POST to Shopify Price Rules API, and Module 7 is an HTTP POST to the Discount Codes sub-resource of the returned Price Rule ID.

Common Pitfalls & Troubleshooting

A 401 Unauthorized from Shopify's Admin API means either the X-Shopify-Access-Token header is absent or the token has been revoked. Custom App tokens in Shopify do not expire but can be revoked from the Develop Apps panel in the store Admin. Verify the token independently by calling GET https://{your-store}.myshopify.com/admin/api/2024-04/shop.json; a 200 OK response confirms authentication. A 403 Forbidden on a specific endpoint means your app's access scopes are insufficient—return to the Custom App configuration in the Shopify Admin, add the missing scope (e.g., write_discounts), and reinstall the app to obtain a new access token with the updated scope set. The old token is invalidated upon reinstallation.

A 422 Unprocessable Entity when updating Shopify customer tags almost always means the final tags string exceeds Shopify's 255-character limit. Implement a guard in your middleware that checks the reconstructed tags string length before submitting the PUT request and rotates or removes older klaviyo-prefixed tags when the limit is approached.

A 429 Too Many Requests from Shopify signals that you've exceeded the REST API's leaky bucket rate limit. Each Shopify store receives a call bucket of 40 credits refilling at 2 credits per second. Monitor the X-Shopify-Shop-Api-Call-Limit response header (in the format used/bucket_size) on every response to track consumption in real time. For the batch metafield sync use case—which involves updating potentially thousands of customer records—switch from REST API individual PUT calls to the GraphQL Admin API's customerUpdate mutation batched via bulkOperationRunMutation. This endpoint processes the entire dataset as a single asynchronous job and sidesteps per-call rate limiting entirely, at the cost of receiving results via a polling or webhook mechanism rather than synchronously.

On the Klaviyo side, a 400 Bad Request when patching profile properties usually means the property key contains disallowed characters. Klaviyo custom property keys must not contain spaces, dots, or brackets. Use snake_case or camelCase conventions exclusively (for example, use discount_code not discount.code and not Discount Code). If a 429 is returned from Klaviyo's Profiles API during high-volume batch operations, implement a 500ms delay between sequential API calls and use Klaviyo's bulk profile import endpoint for large datasets rather than individual PATCH operations.