Agent Skills: Klaviyo Migration Deep Dive

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/klaviyo-migration-deep-dive

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/klaviyo-pack/skills/klaviyo-migration-deep-dive

Skill Files

Browse the full folder contents for klaviyo-migration-deep-dive.

Download Skill

Loading file tree…

plugins/saas-packs/klaviyo-pack/skills/klaviyo-migration-deep-dive/SKILL.md

Skill Metadata

Name
klaviyo-migration-deep-dive
Description
'Use when you are moving an email/CDP stack onto Klaviyo — off the

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

| 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);
    
  2. 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.

  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

| 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 (+<countrycode><subscriber>) | | 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