Skip to main content
instantly-migration-deep-dive Execute complex Instantly.ai migration strategies for platform changes.
Use when migrating between cold email platforms, consolidating workspaces,
or re-architecting outreach infrastructure around Instantly.
Trigger with phrases like "migrate to instantly", "instantly migration",
"switch to instantly", "instantly platform migration", "outreach migration".
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Related occupations SOC
Based on SOC occupation classification
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill instantly-migration-deep-diveThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository Implement user sign-up and sign-in flows with Clerk.
Use when building authentication UI, customizing sign-in experience,
or implementing OAuth social login.
Trigger with phrases like "clerk sign-in", "clerk sign-up",
"clerk login flow", "clerk OAuth", "clerk social login".
Implement session management and middleware with Clerk.
Use when managing user sessions, configuring route protection,
or implementing token refresh and custom JWT templates.
Trigger with phrases like "clerk session", "clerk middleware",
"clerk route protection", "clerk token", "clerk JWT".
Configure enterprise SSO, role-based access control, and organization management.
Use when implementing SSO integration, configuring role-based permissions,
or setting up organization-level controls.
Trigger with phrases like "clerk SSO", "clerk RBAC",
"clerk enterprise", "clerk roles", "clerk permissions", "clerk organizations".
name instantly-migration-deep-dive description Execute complex Instantly.ai migration strategies for platform changes.
Use when migrating between cold email platforms, consolidating workspaces,
or re-architecting outreach infrastructure around Instantly.
Trigger with phrases like "migrate to instantly", "instantly migration",
"switch to instantly", "instantly platform migration", "outreach migration".
allowed-tools Read, Write, Edit, Bash(npm:*), Bash(curl:*), Grep version 1.12.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","instantly","migration","architecture"] compatibility Designed for Claude Code, also compatible with Codex and OpenClaw
Instantly Migration Deep Dive
Overview
Strategies for migrating to/from Instantly or consolidating multiple outreach tools into Instantly. Covers data migration (leads, campaigns, templates), account migration (email infrastructure), analytics preservation, and parallel-run strategies with zero-downtime cutover.
Migration Scenarios
Scenario Complexity Duration Lemlist/Woodpecker/Mailshake to Instantly Medium 1-2 weeks Salesloft/Outreach to Instantly High 2-4 weeks Multiple Instantly workspaces to one Low 1 week Manual outreach to Instantly automation Low 3-5 days
Instructions
Step 1: Pre-Migration Audit
import { InstantlyClient } from "./src/instantly/client" ;
const client = new InstantlyClient ();
interface MigrationPlan {
sourceLeadCount : number ;
sourceCampaignCount : number ;
sourceTemplateCount : number ;
emailAccountsToMigrate : string [];
estimatedDuration : string ;
risks : string [];
}
async function preMigrationAudit ( ): Promise <MigrationPlan > {
const existingCampaigns = await client.campaigns . ( );
existingAccounts = client. . ( );
. ( );
. ( );
. ( );
(existingAccounts. > ) {
warmup = client. . (
existingAccounts. ( a. )
);
. ( );
}
. ( );
. ( );
. ( );
. ( );
. ( );
. ( );
. ( );
. ( );
. ( );
{
: ,
: ,
: ,
: [],
: ,
: [
,
,
,
],
};
}
list
100
const
await
accounts
list
100
console
log
"=== Current Instantly Workspace ==="
console
log
`Campaigns: ${existingCampaigns.length} `
console
log
`Accounts: ${existingAccounts.length} `
if
length
0
const
await
accounts
warmupAnalytics
map
(a ) =>
email
console
log
`Accounts with warmup: ${(warmup as any []).length} `
console
log
"\n=== Migration Checklist ==="
console
log
"[ ] Export leads from source platform (CSV)"
console
log
"[ ] Export campaign templates and sequences"
console
log
"[ ] Document sending schedules and daily limits"
console
log
"[ ] Export analytics/historical data for reference"
console
log
"[ ] Identify email accounts to migrate (IMAP/SMTP creds)"
console
log
"[ ] Map custom fields to Instantly custom_variables"
console
log
"[ ] Create block list from source platform unsubscribes"
console
log
"[ ] Plan warmup period (14+ days for new accounts)"
return
sourceLeadCount
0
sourceCampaignCount
0
sourceTemplateCount
0
emailAccountsToMigrate
estimatedDuration
"2 weeks"
risks
"Warmup period delays campaign launch by 14+ days"
"Custom field mapping may lose data if not 1:1"
"Sending reputation doesn't transfer between platforms"
Step 2: Import Email Accounts
async function migrateEmailAccounts (accounts : Array <{
email: string ;
first_name: string ;
last_name: string ;
smtp_host: string ;
smtp_port: number ;
smtp_username: string ;
smtp_password: string ;
imap_host: string ;
imap_port: number ;
imap_username: string ;
imap_password: string ;
daily_limit: number ;
}> ) {
const results = { added : 0 , failed : 0 , errors : [] as string [] };
for (const account of accounts) {
try {
await client.request ("/accounts" , {
method : "POST" ,
body : JSON .stringify ({
email : account.email ,
first_name : account.first_name ,
last_name : account.last_name ,
smtp_host : account.smtp_host ,
smtp_port : account.smtp_port ,
smtp_username : account.smtp_username ,
smtp_password : account.smtp_password ,
imap_host : account.imap_host ,
imap_port : account.imap_port ,
imap_username : account.imap_username ,
imap_password : account.imap_password ,
daily_limit : account.daily_limit ,
}),
});
results.added ++;
console .log (`Added: ${account.email} ` );
} catch (e : any ) {
results.failed ++;
results.errors .push (`${account.email} : ${e.message} ` );
}
}
if (results.added > 0 ) {
const addedEmails = accounts
.slice (0 , results.added )
.map ((a ) => a.email );
await client.accounts .enableWarmup (addedEmails);
console .log (`Warmup enabled for ${addedEmails.length} accounts` );
}
console .log (`\nAccount migration: ${results.added} added, ${results.failed} failed` );
return results;
}
async function migrateWithOAuth (provider : "google" | "microsoft" ) {
const endpoint = provider === "google"
? "/oauth/google/init"
: "/oauth/microsoft/init" ;
const session = await client.request <{ session_id : string ; redirect_url : string }>(
endpoint,
{ method : "POST" }
);
console .log (`OAuth flow started. Redirect user to: ${session.redirect_url} ` );
console .log (`Session ID: ${session.session_id} ` );
let status = await client.request (`/oauth/session/status/${session.session_id} ` );
console .log ("Session status:" , status);
}
Step 3: Import Leads from CSV import { readFileSync } from "fs" ;
import { parse } from "csv-parse/sync" ;
async function importLeadsFromCSV (
filePath : string ,
campaignId : string ,
fieldMapping : Record <string , string >
) {
const csv = readFileSync (filePath, "utf-8" );
const records = parse (csv, { columns : true , skip_empty_lines : true });
console .log (`Importing ${records.length} leads from ${filePath} ` );
const results = { added : 0 , skipped : 0 , failed : 0 };
for (let i = 0 ; i < records.length ; i++) {
const row = records[i];
try {
const lead : Record <string , unknown > = {
campaign : campaignId,
skip_if_in_workspace : true ,
verify_leads_on_import : true ,
};
for (const [csvCol, instantlyField] of Object .entries (fieldMapping)) {
if (row[csvCol]) {
lead[instantlyField] = row[csvCol];
}
}
const standardFields = ["email" , "first_name" , "last_name" , "company_name" , "website" , "phone" ];
const customVars : Record <string , string > = {};
for (const [csvCol, instantlyField] of Object .entries (fieldMapping)) {
if (!standardFields.includes (instantlyField) && row[csvCol]) {
customVars[instantlyField] = row[csvCol];
}
}
if (Object .keys (customVars).length > 0 ) {
lead.custom_variables = customVars;
}
await client.request ("/leads" , {
method : "POST" ,
body : JSON .stringify (lead),
});
results.added ++;
} catch (e : any ) {
if (e.message .includes ("422" )) {
results.skipped ++;
} else {
results.failed ++;
}
}
if ((i + 1 ) % 100 === 0 ) {
console .log (`Progress: ${i + 1 } /${records.length} ` );
}
}
console .log (`Import complete: ${results.added} added, ${results.skipped} skipped, ${results.failed} failed` );
}
const mapping = {
"Email" : "email" ,
"First Name" : "first_name" ,
"Last Name" : "last_name" ,
"Company" : "company_name" ,
"Website" : "website" ,
"Title" : "title" ,
"Industry" : "industry" ,
};
Step 4: Migrate Unsubscribes to Block List async function migrateUnsubscribes (unsubscribedEmails : string [] ) {
console .log (`Migrating ${unsubscribedEmails.length} unsubscribes to block list` );
const batchSize = 100 ;
for (let i = 0 ; i < unsubscribedEmails.length ; i += batchSize) {
const batch = unsubscribedEmails.slice (i, i + batchSize);
await client.request ("/block-lists-entries/bulk-create" , {
method : "POST" ,
body : JSON .stringify ({ entries : batch }),
});
console .log (`Batch ${Math .floor(i / batchSize) + 1 } : ${batch.length} entries added` );
}
console .log ("Block list migration complete" );
}
Step 5: Parallel Run Strategy
async function parallelRunMonitor ( ) {
console .log ("=== Parallel Run Status ===\n" );
const campaigns = await client.campaigns .list (50 );
const activeCampaigns = campaigns.filter ((c ) => c.status === 1 );
for (const campaign of activeCampaigns) {
const analytics = await client.campaigns .analytics (campaign.id );
const sent = analytics.emails_sent || 1 ;
console .log (`${campaign.name} :` );
console .log (` Sent: ${analytics.emails_sent} | Open: ${((analytics.emails_opened / sent) * 100 ).toFixed(1 )} % | Reply: ${((analytics.emails_replied / sent) * 100 ).toFixed(1 )} %` );
}
console .log ("\n=== Cutover Checklist ===" );
console .log ("[ ] Instantly campaigns matching old platform performance" );
console .log ("[ ] All leads migrated and deduplicated" );
console .log ("[ ] Webhooks delivering to CRM correctly" );
console .log ("[ ] Block list complete (all unsubscribes migrated)" );
console .log ("[ ] Warmup healthy (>80% inbox rate) for all accounts" );
console .log ("[ ] Old platform campaigns paused" );
console .log ("[ ] Team trained on Instantly dashboard" );
}
Migration Timeline Week 1: Setup & Warmup
- Add email accounts to Instantly
- Enable warmup (runs for 14+ days)
- Import block list / unsubscribes
- Map custom fields
Week 2: Data Migration
- Import leads from CSV
- Recreate campaign templates as sequences
- Set up webhooks and CRM integration
- Configure sending schedules
Week 3: Parallel Run
- Launch test campaign on Instantly (small list)
- Compare metrics with old platform
- Fix any delivery or integration issues
Week 4: Cutover
- Pause old platform campaigns
- Activate full Instantly campaigns
- Monitor for 48 hours
- Decommission old platform
Error Handling Error Cause Solution Account SMTP auth fails Wrong credentials from old platform Re-check app password or OAuth flow Leads rejected as duplicates Already imported Use skip_if_in_workspace: true Custom fields lost No matching Instantly field Map to custom_variables object Performance worse than old platform Accounts not warmed up Wait 14+ days, check inbox rates
Resources
Next Steps After migration, set up monitoring with instantly-observability.