klaviyo-migration-deep-dive

v2026.09.24

Use when you are moving an email/CDP stack onto Klaviyo — off the deprecated v1/v2 APIs, off a competitor ESP (Mailchimp, SendGrid), or re-platforming gradually with the strangler fig pattern — and need field mapping, batch import, and post-migration validation. Trigger with phrases like "migrate to klaviyo", "klaviyo migration", "switch to klaviyo", "klaviyo replatform", "mailchimp to klaviyo", "legacy to klaviyo", "v1 to v2 klaviyo".

GitHub
Install command
npx skhub add jeremylongshore/klaviyo-migration-deep-dive
Markdown
SKILL.md

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 KLAVIYO_PRIVATE_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

MigrationComplexityDurationRisk
Klaviyo v1/v2 to current APILow-Medium1-2 weeksLow
Mailchimp/SendGrid to KlaviyoMedium2-4 weeksMedium
Custom ESP to KlaviyoHigh4-8 weeksHigh
Full re-platformHigh2-3 monthsHigh

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);
    
  2. API field mapping — rename v1/v2 fields to the current schema: drop the $ prefix, camelCase everything ($first_name → firstName), and nest address fields under location. Full mapping table in references/implementation.md.

  3. 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.

  4. Strangler fig pattern — route traffic through a MigrationRouter behind a feature flag, ramping Klaviyo from 0% to 100% while optionally dual-writing for comparison.

  5. 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

IssueCauseSolution
Duplicate profilesSame email imported twiceUse createOrUpdateProfile (upsert)
Phone format errorsNon-E.164 formatPre-validate and format to E.164 (+<countrycode><subscriber>)
Rate limited during importToo fastReduce batch size, add delays
Missing consent timestampsHistorical dataSet historicalImport: true flag
Template rendering errorsIncompatible template syntaxConvert 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

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/.curated/klaviyo-migration-deep-dive

Default branch

main

Latest commit

e5a6c3b

Tree SHA

c2dc8e8