Klaviyo E-Commerce Marketing Skill Load with: base.md + (typescript.md or python.md) For integrating Klaviyo email/SMS marketing - customer profiles, event tracking, campaigns, flows, and segmentation. Sources: Klaviyo API Docs | API Reference Why Klaviyo Feature Benefit E-commerce Native Built for online stores, deep integrations Event-Based Trigger flows from any customer action Segmentation Advanced filtering on behavior + properties Email + SMS Unified platform for both channels Analytics Revenue attribution per campaign API Basics Base URLs Type URL Server-side (Private) https://a.klaviyo.com/api Client-side (Public) https://a.klaviyo.com/client Authentication // Server-side: Private API Key const headers = { "Authorization" : "Klaviyo-API-Key pk_xxxxxxxxxxxxxxxxxxxxxxxx" , "Content-Type" : "application/json" , "revision" : "2024-10-15" , // API version } ; // Client-side: Public API Key (6 characters) const publicKey = "XXXXXX" ; // Company ID // Use as query param: ?company_id=XXXXXX API Key Scopes Scope Access Read-only View data only Full Read + write (default) Custom Specific permissions Installation Node.js npm install klaviyo-api // lib/klaviyo.ts import { ApiClient , EventsApi , ProfilesApi , ListsApi } from "klaviyo-api" ; const client = new ApiClient ( ) ; client . setApiKey ( process . env . KLAVIYO_PRIVATE_KEY ! ) ; export const eventsApi = new EventsApi ( client ) ; export const profilesApi = new ProfilesApi ( client ) ; export const listsApi = new ListsApi ( client ) ; Python pip install klaviyo-api
lib/klaviyo.py
from
klaviyo_api
import
KlaviyoAPI
klaviyo
=
KlaviyoAPI
(
api_key
=
os
.
environ
[
"KLAVIYO_PRIVATE_KEY"
]
,
max_delay
=
60
,
max_retries
=
3
)
Direct HTTP (Any Language)
// lib/klaviyo.ts
const
KLAVIYO_BASE_URL
=
"https://a.klaviyo.com/api"
;
async
function
klaviyoRequest
(
endpoint
:
string
,
method
:
"GET"
|
"POST"
|
"PATCH"
|
"DELETE"
=
"GET"
,
body
?
:
object
)
{
const
response
=
await
fetch
(
${
KLAVIYO_BASE_URL
}
${
endpoint
}
,
{
method
,
headers
:
{
Authorization
:
Klaviyo-API-Key
${
process
.
env
.
KLAVIYO_PRIVATE_KEY
}
,
"Content-Type"
:
"application/json"
,
revision
:
"2024-10-15"
,
}
,
body
:
body
?
JSON
.
stringify
(
body
)
:
undefined
,
}
)
;
if
(
!
response
.
ok
)
{
const
error
=
await
response
.
json
(
)
;
throw
new
Error
(
Klaviyo API error:
${
JSON
.
stringify
(
error
)
}
)
;
}
return
response
.
json
(
)
;
}
Profiles (Customers)
Create/Update Profile
// Upsert profile (create or update)
async
function
upsertProfile
(
data
:
ProfileInput
)
{
return
klaviyoRequest
(
"/profiles"
,
"POST"
,
{
data
:
{
type
:
"profile"
,
attributes
:
{
email
:
data
.
email
,
phone_number
:
data
.
phone
,
// E.164 format: +1234567890
first_name
:
data
.
firstName
,
last_name
:
data
.
lastName
,
properties
:
{
// Custom properties
lifetime_value
:
data
.
ltv
,
plan
:
data
.
plan
,
signup_source
:
data
.
source
,
}
,
location
:
{
city
:
data
.
city
,
region
:
data
.
state
,
country
:
data
.
country
,
zip
:
data
.
zip
,
}
,
}
,
}
,
}
)
;
}
Python
def
upsert_profile
(
data
)
:
return
klaviyo
.
Profiles
.
create_or_update_profile
(
{
"data"
:
{
"type"
:
"profile"
,
"attributes"
:
{
"email"
:
data
[
"email"
]
,
"first_name"
:
data
[
"first_name"
]
,
"last_name"
:
data
[
"last_name"
]
,
"properties"
:
{
"plan"
:
data
.
get
(
"plan"
)
,
}
}
}
}
)
Get Profile
async
function
getProfileByEmail
(
email
:
string
)
{
const
response
=
await
klaviyoRequest
(
/profiles?filter=equals(email,"
${
email
}
")
)
;
return
response
.
data
[
0
]
;
}
async
function
getProfileById
(
profileId
:
string
)
{
return
klaviyoRequest
(
/profiles/
${
profileId
}
)
;
}
Update Profile Properties
async
function
updateProfileProperties
(
profileId
:
string
,
properties
:
Record
<
string
,
any
) { return klaviyoRequest (
/profiles/ ${ profileId }, "PATCH" , { data : { type : "profile" , id : profileId , attributes : { properties , } , } , } ) ; } // Usage await updateProfileProperties ( "profile_id" , { last_purchase_date : new Date ( ) . toISOString ( ) , total_orders : 5 , vip_status : true , } ) ; Events (Tracking) Track Event (Server-Side) async function trackEvent ( data : EventInput ) { return klaviyoRequest ( "/events" , "POST" , { data : { type : "event" , attributes : { profile : { data : { type : "profile" , attributes : { email : data . email , // or phone_number, or external_id } , } , } , metric : { data : { type : "metric" , attributes : { name : data . eventName , } , } , } , properties : data . properties , value : data . value , // For revenue tracking unique_id : data . uniqueId , // Deduplication time : data . timestamp || new Date ( ) . toISOString ( ) , } , } , } ) ; } Common E-Commerce Events // Viewed Product await trackEvent ( { email : customer . email , eventName : "Viewed Product" , properties : { ProductID : product . id , ProductName : product . name , ProductURL : product . url , ImageURL : product . image , Price : product . price , Categories : product . categories , } , } ) ; // Added to Cart await trackEvent ( { email : customer . email , eventName : "Added to Cart" , properties : { ProductID : product . id , ProductName : product . name , Quantity : quantity , Price : product . price , CartTotal : cart . total , ItemNames : cart . items . map ( i => i . name ) , } , value : product . price * quantity , } ) ; // Started Checkout await trackEvent ( { email : customer . email , eventName : "Started Checkout" , properties : { CheckoutURL : checkout . url , ItemCount : cart . itemCount , Categories : cart . categories , ItemNames : cart . items . map ( i => i . name ) , } , value : cart . total , } ) ; // Placed Order await trackEvent ( { email : customer . email , eventName : "Placed Order" , properties : { OrderId : order . id , ItemCount : order . itemCount , Categories : order . categories , ItemNames : order . items . map ( i => i . name ) , Items : order . items . map ( i => ( { ProductID : i . productId , ProductName : i . name , Quantity : i . quantity , Price : i . price , ImageURL : i . image , ProductURL : i . url , } ) ) , BillingAddress : order . billingAddress , ShippingAddress : order . shippingAddress , } , value : order . total , uniqueId : order . id , // Prevent duplicate orders } ) ; // Fulfilled Order await trackEvent ( { email : customer . email , eventName : "Fulfilled Order" , properties : { OrderId : order . id , TrackingNumber : fulfillment . trackingNumber , TrackingURL : fulfillment . trackingUrl , Carrier : fulfillment . carrier , } , } ) ; // Cancelled Order await trackEvent ( { email : customer . email , eventName : "Cancelled Order" , properties : { OrderId : order . id , Reason : cancellation . reason , } , value : - order . total , // Negative value for refunds } ) ; Client-Side Tracking (JavaScript)
< script async src = " https://static.klaviyo.com/onsite/js/klaviyo.js?company_id=XXXXXX "
</ script
< script
// Identify user klaviyo . identify ( { email : "customer@example.com" , first_name : "John" , last_name : "Doe" , } ) ; // Track event klaviyo . track ( "Viewed Product" , { ProductID : "prod_123" , ProductName : "Blue T-Shirt" , Price : 29.99 , } ) ; // Track with value klaviyo . track ( "Added to Cart" , { ProductID : "prod_123" , ProductName : "Blue T-Shirt" , Price : 29.99 , $value : 29.99 , // Revenue tracking } ) ; </ script
Lists & Segments Add Profile to List async function addToList ( listId : string , emails : string [ ] ) { return klaviyoRequest (
/lists/ ${ listId } /relationships/profiles, "POST" , { data : emails . map ( email => ( { type : "profile" , attributes : { email } , } ) ) , } ) ; } // By profile ID async function addProfileToList ( listId : string , profileId : string ) { return klaviyoRequest (/lists/ ${ listId } /relationships/profiles, "POST" , { data : [ { type : "profile" , id : profileId } ] , } ) ; } Remove from List async function removeFromList ( listId : string , profileId : string ) { return klaviyoRequest (/lists/ ${ listId } /relationships/profiles, "DELETE" , { data : [ { type : "profile" , id : profileId } ] , } ) ; } Get List Members async function getListMembers ( listId : string , cursor ? : string ) { const params = new URLSearchParams ( { "page[size]" : "100" , } ) ; if ( cursor ) { params . set ( "page[cursor]" , cursor ) ; } return klaviyoRequest (/lists/ ${ listId } /profiles? ${ params }) ; } Create List async function createList ( name : string ) { return klaviyoRequest ( "/lists" , "POST" , { data : { type : "list" , attributes : { name } , } , } ) ; } Campaigns Get Campaigns async function getCampaigns ( status ? : "draft" | "scheduled" | "sent" ) { const params = new URLSearchParams ( ) ; if ( status ) { params . set ( "filter" ,equals(status," ${ status } ")) ; } return klaviyoRequest (/campaigns? ${ params }) ; } Get Campaign Performance async function getCampaignMetrics ( campaignId : string ) { return klaviyoRequest (/campaign-recipient-estimations/ ${ campaignId }, "GET" ) ; } Flows (Automations) Get Flows async function getFlows ( ) { return klaviyoRequest ( "/flows" ) ; } async function getFlowById ( flowId : string ) { return klaviyoRequest (/flows/ ${ flowId }) ; } Common Flow Triggers Flow Type Trigger Event Welcome Series Added to List Abandoned Cart Added to Cart + No Purchase Browse Abandon Viewed Product + No Cart Post-Purchase Placed Order Winback No Order in X Days Review Request Fulfilled Order Webhooks Create Webhook async function createWebhook ( data : WebhookInput ) { return klaviyoRequest ( "/webhooks" , "POST" , { data : { type : "webhook" , attributes : { name : data . name , endpoint_url : data . url , secret_key : data . secret , topics : data . topics , // e.g., ["profile.created", "event.created"] } , } , } ) ; } Webhook Topics Topic Trigger profile.created New profile created profile.updated Profile properties changed profile.merged Profiles merged event.created New event tracked list.member.added Profile added to list list.member.removed Profile removed from list Verify Webhook Signature import crypto from "crypto" ; function verifyKlaviyoWebhook ( payload : string , signature : string , secret : string ) : boolean { const expectedSignature = crypto . createHmac ( "sha256" , secret ) . update ( payload ) . digest ( "base64" ) ; return crypto . timingSafeEqual ( Buffer . from ( signature ) , Buffer . from ( expectedSignature ) ) ; } // Express handler app . post ( "/webhooks/klaviyo" , ( req , res ) => { const signature = req . headers [ "klaviyo-webhook-signature" ] as string ; if ( ! verifyKlaviyoWebhook ( JSON . stringify ( req . body ) , signature , WEBHOOK_SECRET ) ) { return res . status ( 401 ) . json ( { error : "Invalid signature" } ) ; } const { type , data } = req . body ; switch ( type ) { case "profile.created" : handleNewProfile ( data ) ; break ; case "event.created" : handleNewEvent ( data ) ; break ; } res . status ( 200 ) . json ( { received : true } ) ; } ) ; Rate Limits Window Limit Burst 75 requests/second Steady 700 requests/minute Handle Rate Limiting async function klaviyoRequestWithRetry ( endpoint : string , method : "GET" | "POST" | "PATCH" | "DELETE" = "GET" , body ? : object , retries = 3 ) : Promise < any{ for ( let attempt = 0 ; attempt < retries ; attempt ++ ) { const response = await fetch (
${ KLAVIYO_BASE_URL } ${ endpoint }, { method , headers : { Authorization :Klaviyo-API-Key ${ process . env . KLAVIYO_PRIVATE_KEY }, "Content-Type" : "application/json" , revision : "2024-10-15" , } , body : body ? JSON . stringify ( body ) : undefined , } ) ; if ( response . status === 429 ) { const retryAfter = parseInt ( response . headers . get ( "Retry-After" ) || "5" ) ; await new Promise ( r => setTimeout ( r , retryAfter * 1000 ) ) ; continue ; } if ( ! response . ok ) { throw new Error (Klaviyo error: ${ response . status }) ; } return response . json ( ) ; } throw new Error ( "Max retries exceeded" ) ; } Pagination async function getAllProfiles ( ) { const profiles = [ ] ; let cursor : string | undefined ; do { const params = new URLSearchParams ( { "page[size]" : "100" } ) ; if ( cursor ) { params . set ( "page[cursor]" , cursor ) ; } const response = await klaviyoRequest (/profiles? ${ params }) ; profiles . push ( ... response . data ) ; cursor = response . links ?. next ? new URL ( response . links . next ) . searchParams . get ( "page[cursor]" ) : undefined ; } while ( cursor ) ; return profiles ; } Filtering & Sorting // Filter by date const recentEvents = await klaviyoRequest (/events?filter=greater-than(datetime,2024-01-01T00:00:00Z)) ; // Filter by property const vipProfiles = await klaviyoRequest (/profiles?filter=equals(properties.vip_status,true)) ; // Multiple filters (AND) const filtered = await klaviyoRequest (/profiles?filter=and(equals(properties.plan,"pro"),greater-than(properties.ltv,1000))) ; // Sorting const sorted = await klaviyoRequest (/profiles?sort=-created// Descending by created date ) ; // Sparse fieldsets (only return specific fields) const sparse = await klaviyoRequest (/profiles?fields[profile]=email,first_name,properties) ; Integration Patterns E-Commerce Order Sync // After order is placed async function syncOrderToKlaviyo ( order : Order ) { // 1. Upsert customer profile await upsertProfile ( { email : order . customerEmail , firstName : order . customerFirstName , lastName : order . customerLastName , phone : order . customerPhone , } ) ; // 2. Update lifetime metrics await updateProfileProperties ( await getProfileIdByEmail ( order . customerEmail ) , { last_order_date : new Date ( ) . toISOString ( ) , total_orders : order . customerOrderCount , lifetime_value : order . customerLifetimeValue , } ) ; // 3. Track order event await trackEvent ( { email : order . customerEmail , eventName : "Placed Order" , properties : { OrderId : order . id , Items : order . items , // ... other properties } , value : order . total , uniqueId : order . id , } ) ; } Subscription Status Sync // When subscription changes async function syncSubscriptionStatus ( user : User , status : string ) { await updateProfileProperties ( user . klaviyoProfileId , { subscription_status : status , subscription_plan : user . plan , subscription_updated_at : new Date ( ) . toISOString ( ) , } ) ; await trackEvent ( { email : user . email , eventName :Subscription ${ status }, properties : { plan : user . plan , mrr : user . mrr , } , value : status === "cancelled" ? - user . mrr : user . mrr , } ) ; } Environment Variables
.env
KLAVIYO_PRIVATE_KEY
pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx KLAVIYO_PUBLIC_KEY = XXXXXX KLAVIYO_WEBHOOK_SECRET = your_webhook_secret Add to credentials.md : 'KLAVIYO_PRIVATE_KEY' : r'pk_[a-f0-9]{32}' , 'KLAVIYO_PUBLIC_KEY' : r'[A-Z0-9]{6}' , Checklist Setup Klaviyo account created Private API key generated Public API key noted (company ID) API revision set in headers Integration Profile sync on signup/update Key events tracked (view, cart, order) Order events include Items array Revenue tracked with $value Unique IDs for deduplication Testing Test profile creation Test event tracking Verify events in Klaviyo dashboard Test webhook delivery Test rate limit handling Anti-Patterns Missing email/phone - Every profile needs at least one identifier Duplicate events - Use unique_id for orders/transactions Missing Items array - Required for product recommendations Client-side only - Server-side tracking is more reliable Ignoring rate limits - Implement exponential backoff Hardcoded API keys - Use environment variables Missing revenue tracking - Include $value for ROI attribution