Skip to main content
instantly-incident-runbook Execute Instantly.ai incident response procedures with triage, mitigation, and recovery.
Use when responding to campaign failures, account health crises,
deliverability drops, or Instantly API outages.
Trigger with phrases like "instantly incident", "instantly outage",
"instantly campaign failed", "instantly emergency", "instantly runbook".
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
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-incident-runbookThe 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... Related occupations SOC
Based on SOC occupation classification
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-incident-runbook description Execute Instantly.ai incident response procedures with triage, mitigation, and recovery.
Use when responding to campaign failures, account health crises,
deliverability drops, or Instantly API outages.
Trigger with phrases like "instantly incident", "instantly outage",
"instantly campaign failed", "instantly emergency", "instantly runbook".
allowed-tools Read, Write, Edit, Bash(curl:*), Bash(npm:*), Grep version 1.12.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","instantly","incident-response","runbook"] compatibility Designed for Claude Code, also compatible with Codex and OpenClaw
Instantly Incident Runbook
Overview
Structured incident response procedures for Instantly.ai integration failures. Covers campaign pause cascades, account health crises, bounce protect triggers, webhook delivery failures, and API outages.
Severity Levels
Severity Criteria Response Time Examples P1 Critical All campaigns stopped, sending halted 15 min All accounts unhealthy, API 5xx P2 High Multiple campaigns affected 1 hour Bounce protect on key campaign, warmup degraded P3 Medium Single campaign/account issue 4 hours One account SMTP failure, webhook delivery issue P4 Low Non-blocking issue Next business day Analytics gap, cosmetic dashboard issue
Incident: All Campaigns in Accounts Unhealthy (-1)
Triage
import { InstantlyClient } from "./src/instantly/client" ;
const client = new InstantlyClient ();
async function triageCampaignHealth ( ) {
console .log ("=== P1 TRIAGE: Campaign Health ===\n" );
const campaigns = await client.campaigns .list (100 );
const statusCounts : Record <number , number > = {};
for (const c of campaigns) {
statusCounts[c. ] = (statusCounts[c. ] || ) + ;
}
. ( , statusCounts);
unhealthy = campaigns. ( c. === - );
. ( );
accounts = client. . ( );
vitals = client. . (accounts. ( a. ));
broken = (vitals []). ( v. !== || v. !== );
healthy = (vitals []). ( v. === && v. === );
. ( );
(broken. > ) {
. ( );
( v broken) {
. ( );
}
}
{ unhealthy, broken, healthy };
}
status
status
0
1
console
log
"Campaign status distribution:"
const
filter
(c ) =>
status
1
console
log
`Unhealthy campaigns: ${unhealthy.length} `
const
await
accounts
list
200
const
await
accounts
testVitals
map
(a ) =>
email
const
as
any
filter
(v ) =>
smtp_status
"ok"
imap_status
"ok"
const
as
any
filter
(v ) =>
smtp_status
"ok"
imap_status
"ok"
console
log
`\nAccounts: ${accounts.length} total, ${healthy.length} healthy, ${broken.length} broken`
if
length
0
console
log
"\nBroken accounts:"
for
const
of
console
log
` ${v.email} : SMTP=${v.smtp_status} IMAP=${v.imap_status} DNS=${v.dns_status} `
return
Mitigation async function mitigateBrokenAccounts ( ) {
const { broken, healthy } = await triageCampaignHealth ();
for (const v of broken) {
try {
await client.accounts .pause (v.email );
console .log (`Paused broken account: ${v.email} ` );
} catch (e : any ) {
console .log (`Failed to pause ${v.email} : ${e.message} ` );
}
}
if (healthy.length < 3 ) {
console .log ("\nWARNING: Fewer than 3 healthy accounts. Campaign performance will be degraded." );
console .log ("Action: Fix broken account credentials or add new accounts." );
}
console .log ("\nTo resume fixed accounts:" );
for (const v of broken) {
console .log (` POST /accounts/${encodeURIComponent (v.email)} /resume` );
}
console .log ("\nAfter accounts are fixed, reactivate campaigns:" );
console .log (" POST /campaigns/{id}/activate" );
}
Incident: Bounce Protect Triggered (-2)
Triage & Response async function handleBounceProtect ( ) {
console .log ("=== P2 TRIAGE: Bounce Protect ===\n" );
const campaigns = await client.campaigns .list (100 );
const bounceProtected = campaigns.filter ((c ) => c.status === -2 );
for (const campaign of bounceProtected) {
const analytics = await client.campaigns .analytics (campaign.id );
const bounceRate = ((analytics.emails_bounced / analytics.emails_sent ) * 100 ).toFixed (1 );
console .log (`${campaign.name} : ${bounceRate} % bounce rate` );
console .log (` Sent: ${analytics.emails_sent} , Bounced: ${analytics.emails_bounced} ` );
const leads = await client.leads .list ({
campaign : campaign.id ,
limit : 100 ,
});
const bouncedLeads = leads.filter ((l ) => l.status === -1 );
console .log (` Bounced leads: ${bouncedLeads.length} of ${leads.length} sampled` );
}
console .log ("\n=== Recovery Steps ===" );
console .log ("1. Export remaining leads and verify emails with external service" );
console .log ("2. Remove bounced/invalid leads from the campaign" );
console .log ("3. Add verified leads back or create new campaign with clean list" );
console .log ("4. Re-activate campaign: POST /campaigns/{id}/activate" );
console .log ("\n=== Prevention ===" );
console .log ("- Set verify_leads_on_import: true on all lead imports" );
console .log ("- Use email verification: POST /api/v2/email-verification" );
console .log ("- Set allow_risky_contacts: false on campaign" );
}
Incident: Webhook Delivery Failure
Triage & Recovery async function handleWebhookFailure ( ) {
console .log ("=== P3 TRIAGE: Webhook Delivery ===\n" );
const webhooks = await client.webhooks .list ();
for (const w of webhooks as any []) {
console .log (`${w.name} : ${w.event_type} -> ${w.target_hook_url} ` );
console .log (` Status: ${w.status || "active" } ` );
}
const summary = await client.request ("/webhook-events/summary" );
console .log ("\nDelivery summary:" , JSON .stringify (summary, null , 2 ));
const byDate = await client.request ("/webhook-events/summary-by-date" );
console .log ("By date:" , JSON .stringify (byDate, null , 2 ));
for (const w of webhooks as any []) {
if (w.status === "paused" ) {
console .log (`\nResuming paused webhook: ${w.name} ` );
try {
await client.request (`/webhooks/${w.id} /resume` , { method : "POST" });
console .log (" Resumed successfully" );
await client.request (`/webhooks/${w.id} /test` , { method : "POST" });
console .log (" Test event sent" );
} catch (e : any ) {
console .log (` Failed: ${e.message} ` );
}
}
}
}
Incident: API Rate Limit Storm (429s)
Response async function handleRateLimitStorm ( ) {
console .log ("=== P2 TRIAGE: Rate Limit Storm ===\n" );
console .log ("Immediate actions:" );
console .log ("1. Stop all automated API calls (pause cron jobs, workers)" );
console .log ("2. Check for runaway loops or misconfigured batch jobs" );
console .log ("3. Implement exponential backoff if not already in place" );
const jobs = await client.request <Array <{
id : string ; status : string ; timestamp_created : string ;
}>>("/background-jobs?limit=20" );
const stuck = jobs.filter ((j ) => j.status === "in_progress" );
console .log (`\nBackground jobs in progress: ${stuck.length} ` );
for (const j of stuck) {
console .log (` ${j.id} : ${j.status} (created: ${j.timestamp_created} )` );
}
console .log ("\nRate limit guidelines:" );
console .log (" - Most endpoints: standard REST limits" );
console .log (" - GET /emails: 20 req/min (strictest)" );
console .log (" - Implement 2^attempt second backoff on 429" );
console .log (" - Add jitter to prevent thundering herd" );
console .log (" - Use request queue with max concurrency of 3-5" );
}
Incident: Warmup Degradation
Response async function handleWarmupDegradation ( ) {
console .log ("=== P2 TRIAGE: Warmup Degradation ===\n" );
const accounts = await client.accounts .list (200 );
const warmupData = await client.accounts .warmupAnalytics (
accounts.map ((a ) => a.email )
) as Array <{
email : string ;
warmup_emails_sent : number ;
warmup_emails_landed_inbox : number ;
warmup_emails_landed_spam : number ;
}>;
const degraded = warmupData.filter ((w ) => {
const sent = w.warmup_emails_sent || 1 ;
return (w.warmup_emails_landed_inbox / sent) < 0.8 ;
});
if (degraded.length > 0 ) {
console .log (`${degraded.length} accounts with low warmup inbox rate:\n` );
for (const w of degraded) {
const rate = ((w.warmup_emails_landed_inbox / (w.warmup_emails_sent || 1 )) * 100 ).toFixed (1 );
console .log (` ${w.email} : ${rate} % inbox rate (${w.warmup_emails_landed_spam} spam)` );
}
console .log ("\n=== Recovery ===" );
console .log ("1. Pause ALL campaigns using degraded accounts" );
console .log ("2. Keep warmup running (don't disable)" );
console .log ("3. Reduce campaign daily_limit to 10-20 per account" );
console .log ("4. Wait 7-14 days for reputation recovery" );
console .log ("5. Re-test inbox rates before re-enabling campaigns" );
console .log ("6. Check DNS: SPF, DKIM, DMARC records are correct" );
} else {
console .log ("All accounts have healthy warmup rates (>80% inbox)" );
}
}
Quick Diagnostic Script set -euo pipefail
echo "=== Instantly Incident Diagnostic ==="
echo "Time: $(date -u) "
echo
echo "--- Campaign Status ---"
curl -s https://api.instantly.ai/api/v2/campaigns?limit =100 \
-H "Authorization: Bearer $INSTANTLY_API_KEY " | \
jq 'group_by(.status) | map({status: .[0].status, count: length})'
echo "--- Account Vitals ---"
EMAILS=$(curl -s https://api.instantly.ai/api/v2/accounts?limit =50 \
-H "Authorization: Bearer $INSTANTLY_API_KEY " | jq -r '[.[].email] | join(",")' )
echo "Accounts: $EMAILS "
echo "--- Webhooks ---"
curl -s https://api.instantly.ai/api/v2/webhooks?limit =20 \
-H "Authorization: Bearer $INSTANTLY_API_KEY " | \
jq '[.[] | {name, event_type, status}]'
Error Handling Error Cause Solution Can't reach API during incident Instantly outage Check status.instantly.ai, wait Can't pause accounts 403 scope errorUse dashboard as fallback Runbook script rate-limited Too many diagnostic calls Space out requests, use backoff
Resources
Next Steps For data handling and compliance, see instantly-data-handling.