Klaviyo Integration - Complete Guide
Priority: P0 - Blocking multiple marketing initiatives Owner: Chip Stakeholder: Morgane (Marketing)
Why This Matters
Morgane's marketing projects are blocked until we can send user behavior data TO Klaviyo:
| Blocked Project | What It Needs |
|---|---|
| Repurchase campaigns | Trigger when test stock is low |
| User reviews + NPS | Survey after test completion |
| In-app notifications | Klaviyo SDK for push messages |
| Revamped onboarding | Behavior-based activation emails |
"The triggers for Klaviyo from the app will be very essential in order to encourage the one-time purchasers to buy again or to swap to subscription!" — Morgane
The Big Picture
Current State: One-Way (Read Only)
Problem: We can read Klaviyo data, but can't push app behavior back to Klaviyo.
Target State: Two-Way Sync
Complete System Flow
This diagram shows everything - from user action to marketing automation:
What Klaviyo Needs to Know
Profile Properties (14 total)
These are the fields we'll sync to each Klaviyo profile:
Property Details
| Property | Type | When Updated | Source |
|---|---|---|---|
total_tests_completed | integer | Every test | Count readings |
last_test_date | datetime | Every test | reading.created |
first_test_completed | boolean | First test | Derived |
days_to_first_test | integer | First test | Shopify order date |
days_since_last_test | integer | Daily batch | Calculated |
avg_tests_per_week | float | Daily batch | 30-day rolling |
test_stock_percentage | integer 0-100 | Every test | Loop or Shopify |
tests_remaining | integer | Every test | Loop or Shopify |
subscription_plan | string | Every test | Loop data |
user_gender | string | Profile update | App onboarding |
user_age | integer | Profile update | App onboarding |
purchase_motivation | string | Profile update | App onboarding |
hormone_tracking_goal | string | Profile update | App onboarding |
app_first_opened | datetime | First open | Firebase event |
Test Stock Calculation
This is the key feature for reorder campaigns:
Trigger Thresholds
| Stock % | Klaviyo Segment | Email Flow |
|---|---|---|
| 75% | stock_warning_75 | Educational nurture |
| 50% | stock_warning_50 | "Halfway through" |
| 25% | stock_warning_25 | Reorder reminder |
| 10% | stock_urgent | Urgent restock |
| 0% | stock_empty | Subscription upsell |
User Segmentation
By Testing Frequency
| Segment | Definition | Klaviyo Condition |
|---|---|---|
| Power users | >3 tests/week | avg_tests_per_week > 3 |
| Active | 1-3 tests/week | avg_tests_per_week >= 1 AND <= 3 |
| Occasional | 1-3 tests/month | avg_tests_per_week < 1 AND days_since_last_test < 30 |
| Inactive | 30+ days no test | days_since_last_test >= 30 |
| Never tested | Purchased, 0 tests | total_tests_completed = 0 |
By Purchase Type
| Segment | Klaviyo Condition |
|---|---|
| One-time buyers | subscription_plan = 'one_time' |
| Light subscribers | subscription_plan = '4_tests' |
| Medium subscribers | subscription_plan = '8_tests' |
| Heavy subscribers | subscription_plan = '12_tests' |
Opportunity Matrix
| Quadrant | Who | Action |
|---|---|---|
| Top-Right | Subscriber + Power User | Retention focus, VIP treatment |
| Top-Left | Subscriber + Inactive | Churn risk, re-engagement |
| Bottom-Right | One-time + Active | Upsell to subscription |
| Bottom-Left | One-time + Never tested | Activation campaign |
Marketing Automation Flows
Flow 1: First Test Completed
Flow 2: Test Stock Depletion
Flow 3: Win-Back Campaign
In-App Notifications (Phase 5b)
The Klaviyo SDK is now installed in eli-app (Phase 5, Zeeshan). The SDK foundation (init, profile, push token) is in place. What remains is enabling in-app message rendering and Klaviyo push handling.
Current state: SDK installed and initialized, profile identification works, push token forwarded. In-app message display and Klaviyo push routing still need to be wired up (Phase 5b).
Email Resolution
~6% of users have different emails in the app vs Shopify. We prioritize Shopify email:
Coverage: 94% of purchasers have linked Shopify accounts.
Data Volumes
From existing BigQuery data:
| Metric | Count |
|---|---|
| Klaviyo profiles | 36,832 |
| Users with completed tests | 1,636 |
| Shopify-linked users | 94% |
| Abandoned checkouts | 870 |
Backfill Strategy
- One-time backfill: Query BigQuery for all historical test data
- Real-time going forward: Hook into test completion flow
The existing user-journey-records.sql query already links:
- Klaviyo profiles → Firebase accounts → Biometrics test data
Confirmed with Morgane
| Item | Status | Notes |
|---|---|---|
| Cart abandonment | Already handled | Klaviyo-Shopify native integration |
| Test kit identification | Use existing query | Same as diurnal curve completion |
| App download tracking | Use first_open | Can't track actual downloads |
| Backfill historical data | Approved | Run once, then real-time |
Implementation Phases
Phase 5: Mobile SDK --- DONE (EXM-996, Zeeshan, 2026-01-27)
Implemented in eli-app on the development branch. PR #586.
What was built:
| Component | File | What It Does |
|---|---|---|
| Klaviyo service | src/services/klaviyo.ts | SDK init, setProfile, createEvent, resetProfile, setPushToken |
| Environment config | src/config/environment.ts, .env-cmdrc.js, src/types/env.d.ts | REACT_APP_KLAVIYO_API_KEY wired through env |
| Profile identification | src/services/auth.ts | setProfile() on all auth flows (email, Google, Apple) + resetProfile() on sign out |
| Push token | src/services/messaging.ts | FCM token forwarded to Klaviyo via setPushToken() |
| Measurement event | src/screens/.../MeasurementCalculating.tsx | measurement_completed event with readingId, hormoneType, userId |
| Notification event | src/services/app.ts | reminder_notification_opened event when user opens a protocol reminder |
| Notification data | src/services/notification.ts, protocolNotifications.ts | Local notifications now carry userId, hormoneType, type in data payload |
Profile data synced to Klaviyo on login:
email,externalId(user.id),firstName,lastName,location.timezone
Events tracked:
measurement_completed--- readingId, hormoneType, userIdreminder_notification_opened--- userId, hormoneType
Remaining mobile work (Phase 5b):
- In-app messages (banners, modals) --- not yet enabled
- Klaviyo push notification handling (
handlePush) --- not yet wired app_first_openedevent --- not yet tracked
Phase 1: Core Test Events (P0) --- NOT STARTED
- Backend: Create
eli-backend-api/src/modules/klaviyo/module - Hook into
readings.controller.ts→handleAnalysis()after test completion - Sync profile properties:
total_tests_completed,last_test_date,first_test_completed,days_to_first_test - Email resolution via Shopify service (94% Shopify email, 6% app email fallback)
Phase 2: Test Stock (P1) --- NOT STARTED
- Calculate from Loop subscriptions or Shopify orders
- Sync:
test_stock_percentage,tests_remaining,subscription_plan
Phase 3: User Demographics (P2) --- NOT STARTED
- Sync on profile updates via
users.controller - Properties:
user_gender,user_age,purchase_motivation,hormone_tracking_goal
Phase 4: Batch Metrics (P3) --- NOT STARTED
- Daily Cloud Scheduler job in eli-kpi
- Properties:
days_since_last_test,avg_tests_per_week
Success Metrics
| Metric | Current | Target |
|---|---|---|
| Reorder trigger latency | Manual | < 1 minute |
| Profile sync success rate | N/A | > 99% |
| Email resolution accuracy | N/A | 94%+ |
| Test stock visibility | 0% | 100% of users |
Related Documents
- Implementation Details - Technical architecture
- Shopify Integration - OAuth for email resolution
- User Journey Data - Data linking architecture
Document History
| Date | Author | Changes |
|---|---|---|
| 2026-01-27 | Chip | Updated with Phase 5 implementation status (EXM-996, Zeeshan). Marked remaining phases. |
| 2025-01-26 | Chip | Consolidated all marketing dashboard docs into single source |