Klaviyo migration deep dive
'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.From its SKILL.md
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill klaviyo-migration-deep-diveAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.4 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it
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-apiSDK 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 asKLAVIYO_PRIVATE_KEY— used by the SDK'sApiKeySession. 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.
-
Legacy v1/v2 to current API — replace deprecated
track/identify/v2 subscribeHTTP calls with theklaviyo-apiSDK (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); -
API field mapping — rename v1/v2 fields to the current schema: drop the
$prefix, camelCase everything ($first_name→firstName), and nest address fields underlocation. Full mapping table in references/implementation.md. -
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. -
Strangler fig pattern — route traffic through a
MigrationRouterbehind a feature flag, ramping Klaviyo from 0% to 100% while optionally dual-writing for comparison. -
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-apiSDK calls, or a competitor-to-Klaviyo transform adapter plus a batch-import runner. - An import result —
{ imported, skipped, failed[] }frommigrateContacts, with thefailedlist 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[] }fromvalidateMigrationcovering 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
identifycall tocreateOrUpdateProfile, showing the field renames. - Feature-flagged cutover — route 10% of events to Klaviyo while campaigns stay legacy.
- Gate a deployment on a
validateMigrationpass.
Minimal first cutover — one profile upsert on the current API:
await profilesApi.createOrUpdateProfile({
data: {
type: 'profile',
attributes: { email: '[email protected]', firstName: 'Jane', properties: { plan: 'pro' } },
},
});
Resources
- Full implementation walkthrough — verbatim code for all five steps + checklist
- Worked examples — end-to-end migration scenarios
- v1/v2 Migration Best Practices
- Relationship Migration Guide
- Custom Integration Guide
- Strangler Fig Pattern
What ships with it: 2 files
12.8 KB alongside SKILL.md
references/
- examples.md3.0 KB
- implementation.md9.9 KB