klaviyo-migration-deep-dive

'Use when you are moving an email/CDP stack onto Klaviyo — off the

Allowed Tools

ReadWriteEditBash(npm:*)Bash(node:*)

Provided by Plugin

klaviyo-pack

Claude Code skill pack for Klaviyo (24 skills)

saas packs v1.7.0
View Plugin

Installation

This skill is included in the klaviyo-pack plugin:

/plugin install klaviyo-pack@claude-code-plugins-plus

Click to copy

Instructions

Klaviyo Migration Deep Dive

Overview

Comprehensive guide for migrating to Klaviyo from legacy APIs (v1/v2), competing ESPs (Mailchimp, SendGrid, etc.), or re-platforming with the strangler fig pattern. Covers data migration, API mapping, batch import, and post-migration validation.

This SKILL.md is the high-level workflow. The full, copy-paste code for every step

lives in references/implementation.md; worked

end-to-end scenarios live in references/examples.md.

Prerequisites

  • Target Klaviyo account configured
  • klaviyo-api SDK installed (npm install klaviyo-api)
  • Source system access for data export
  • Feature flag infrastructure (for gradual rollout)
  • Auth: a Klaviyo private API key (pk***) exported as KLAVIYOPRIVATE_KEY — used by the SDK's ApiKeySession. Legacy v1/v2 calls used a public token in the request body; the current REST API uses the private key in the session header. See references/implementation.md.

Migration Types

Migration Complexity Duration Risk
Klaviyo v1/v2 to current API Low-Medium 1-2 weeks Low
Mailchimp/SendGrid to Klaviyo Medium 2-4 weeks Medium
Custom ESP to Klaviyo High 4-8 weeks High
Full re-platform High 2-3 months High

Instructions

Pick your migration type from the table above, then work the five steps. Each step

has full code in references/implementation.md.

  1. Legacy v1/v2 to current API — replace deprecated track / identify / v2 subscribe HTTP calls with the klaviyo-api SDK (createOrUpdateProfile, createEvent, subscribeProfiles). The session skeleton every step builds on:

   import { ApiKeySession, ProfilesApi, EventsApi } from 'klaviyo-api';

   const session = new ApiKeySession(process.env.KLAVIYO_PRIVATE_KEY!);
   const profilesApi = new ProfilesApi(session);
   const eventsApi = new EventsApi(session);
  1. API field mapping — rename v1/v2 fields to the current schema: drop the $ prefix, camelCase everything ($first_namefirstName), and nest address fields under location. Full mapping table in references/implementation.md.
  2. Competitor migration — write a transform adapter that maps the competitor's contact shape to a Klaviyo profile, then batch-import (50 per batch) with Promise.allSettled, progress logging, and rate-limit delays. Skip suppressed/unsubscribed contacts.
  3. Strangler fig pattern — route traffic through a MigrationRouter behind a feature flag, ramping Klaviyo from 0% to 100% while optionally dual-writing for comparison.
  4. Post-migration validation — run validateMigration() to compare profile counts, sample data integrity, and list membership against the source before decommissioning the legacy system.

Full migration checklist (export → map → import → validate → cut over → decommission) is in references/implementation.md.

Output

Working through this skill produces:

  • Migrated code — v1/v2 HTTP calls replaced with klaviyo-api SDK calls, or a competitor-to-Klaviyo transform adapter plus a batch-import runner.
  • An import result{ imported, skipped, failed[] } from migrateContacts, with the failed list ready for a targeted retry.
  • A MigrationRouter (for gradual cutovers) that routes a configurable percentage of traffic to Klaviyo behind a feature flag.
  • A validation report{ passed, checks[] } from validateMigration covering profile count, data integrity, and list membership, used as the go/no-go gate before decommissioning the legacy system.

Error Handling

Issue Cause Solution
Duplicate profiles Same email imported twice Use createOrUpdateProfile (upsert)
Phone format errors Non-E.164 format Pre-validate and format to E.164 (+)
Rate limited during import Too fast Reduce batch size, add delays
Missing consent timestamps Historical data Set historicalImport: true flag
Template rendering errors Incompatible template syntax Convert to Klaviyo Django template syntax

Examples

Worked, end-to-end scenarios are in references/examples.md:

  • Mailchimp export → Klaviyo import — load a CSV, skip suppressed contacts, batch-import with progress output.
  • Cut over a v1 identify call to createOrUpdateProfile, showing the field renames.
  • Feature-flagged cutover — route 10% of events to Klaviyo while campaigns stay legacy.
  • Gate a deployment on a validateMigration pass.

Minimal first cutover — one profile upsert on the current API:


await profilesApi.createOrUpdateProfile({
  data: {
    type: 'profile',
    attributes: { email: 'user@example.com', firstName: 'Jane', properties: { plan: 'pro' } },
  },
});

Resources

Ready to use klaviyo-pack?