Claude-code-plugins instantly-common-errors

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/instantly-pack/skills/instantly-common-errors" ~/.claude/skills/jeremylongshore-claude-code-plugins-instantly-common-errors && rm -rf "$T"
manifest: plugins/saas-packs/instantly-pack/skills/instantly-common-errors/SKILL.md
source content

Instantly Common Errors

Overview

Diagnostic reference for Instantly API v2 errors. Covers HTTP status codes, campaign state errors, account health issues, lead operation failures, and webhook delivery problems.

Prerequisites

  • Completed
    instantly-install-auth
    setup
  • Access to Instantly dashboard for verification
  • API key with appropriate scopes

HTTP Status Codes

StatusMeaningCommon CauseFix
400
Bad RequestMalformed JSON, invalid field valuesValidate request body against schema
401
UnauthorizedInvalid, expired, or revoked API keyRegenerate key in Settings > Integrations
403
ForbiddenAPI key missing required scopeCreate key with correct scope (e.g.,
campaigns:all
)
404
Not FoundInvalid campaign/lead/account IDVerify resource exists with a GET call first
422
Unprocessable EntityBusiness logic violation (duplicate lead, invalid state)Check error body for details
429
Too Many RequestsRate limit exceededImplement exponential backoff (see below)
500
Internal Server ErrorInstantly server issueRetry with backoff; check status.instantly.ai

Campaign Errors

Campaign Won't Activate (Stuck in Draft)

// Diagnosis: check campaign requirements
async function diagnoseCampaign(campaignId: string) {
  const campaign = await instantly<Campaign>(`/campaigns/${campaignId}`);

  const issues: string[] = [];

  // Check sequences
  if (!campaign.sequences?.length || !campaign.sequences[0]?.steps?.length) {
    issues.push("No email sequences — add at least one step with subject + body");
  }

  // Check schedule
  if (!campaign.campaign_schedule?.schedules?.length) {
    issues.push("No sending schedule — add schedule with timing and days");
  }

  // Check sending accounts
  const mappings = await instantly(`/account-campaign-mappings/${campaignId}`);
  if (!Array.isArray(mappings) || mappings.length === 0) {
    issues.push("No sending accounts assigned — add via PATCH /campaigns/{id} with email_list");
  }

  // Check for leads
  const leads = await instantly<Lead[]>("/leads/list", {
    method: "POST",
    body: JSON.stringify({ campaign: campaignId, limit: 1 }),
  });
  if (leads.length === 0) {
    issues.push("No leads — add leads via POST /leads");
  }

  if (issues.length === 0) {
    console.log("Campaign looks ready to activate");
  } else {
    console.log("Issues preventing activation:");
    issues.forEach((i) => console.log(`  - ${i}`));
  }
}

Campaign Status Codes

StatusLabelMeaning
0
DraftNot yet activated
1
ActiveCurrently sending
2
PausedManually paused
3
CompletedAll leads processed
4
Running SubsequencesMain sequence done, subsequences active
-1
Accounts UnhealthySending accounts have SMTP/IMAP errors
-2
Bounce ProtectAuto-paused due to high bounce rate
-99
SuspendedAccount-level suspension

Fix: Accounts Unhealthy (-1)

async function fixUnhealthyAccounts(campaignId: string) {
  // 1. Get accounts assigned to campaign
  const accounts = await instantly<Account[]>("/accounts?limit=100");

  // 2. Test vitals for each
  const vitals = await instantly("/accounts/test/vitals", {
    method: "POST",
    body: JSON.stringify({ accounts: accounts.map((a) => a.email) }),
  });

  // 3. Identify and fix broken accounts
  for (const v of vitals as any[]) {
    if (v.smtp_status !== "ok" || v.imap_status !== "ok") {
      console.log(`BROKEN: ${v.email} — SMTP=${v.smtp_status}, IMAP=${v.imap_status}`);
      // Pause the broken account
      await instantly(`/accounts/${encodeURIComponent(v.email)}/pause`, { method: "POST" });
      console.log(`  Paused ${v.email}. Fix credentials, then resume.`);
    }
  }
}

Lead Errors

Duplicate Lead (422)

// Prevent duplicates by setting skip flags
await instantly("/leads", {
  method: "POST",
  body: JSON.stringify({
    campaign: campaignId,
    email: "user@example.com",
    first_name: "Jane",
    skip_if_in_workspace: true,   // skip if email exists anywhere in workspace
    skip_if_in_campaign: true,    // skip if already in this campaign
  }),
});

Lead Status Reference

StatusLabelDescription
1
ActiveEligible to receive emails
2
PausedManually paused
3
CompletedAll sequence steps sent
-1
BouncedEmail bounced
-2
UnsubscribedLead unsubscribed
-3
SkippedSkipped (blocklist, duplicate, etc.)

Rate Limit Handling

async function withBackoff<T>(
  operation: () => Promise<T>,
  maxRetries = 5
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (err: any) {
      if (err.status === 429 && attempt < maxRetries) {
        const wait = Math.pow(2, attempt) * 1000;
        console.warn(`429 Rate Limited. Waiting ${wait}ms (attempt ${attempt + 1}/${maxRetries})`);
        await new Promise((r) => setTimeout(r, wait));
        continue;
      }
      throw err;
    }
  }
  throw new Error("Unreachable");
}

Webhook Errors

IssueDiagnosticFix
Events not deliveredCheck webhook status:
GET /webhooks
Webhook may be paused — resume with
POST /webhooks/{id}/resume
Wrong event formatCompare to expected schemaEnsure
event_type
matches:
email_sent
,
reply_received
, etc.
Delivery failuresCheck
GET /webhook-events/summary
Fix target URL, ensure 2xx response within 30s
Retries exhaustingInstantly retries 3x in 30sReturn 200 immediately, process async

Quick Diagnostic Script

set -euo pipefail
echo "=== Instantly Health Check ==="

# Test auth
curl -s -o /dev/null -w "Auth: HTTP %{http_code}\n" \
  https://api.instantly.ai/api/v2/campaigns?limit=1 \
  -H "Authorization: Bearer $INSTANTLY_API_KEY"

# Count campaigns by 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})'

# Check account health
curl -s https://api.instantly.ai/api/v2/accounts?limit=100 \
  -H "Authorization: Bearer $INSTANTLY_API_KEY" | \
  jq '[.[] | {email, status, warmup_status}] | .[:5]'

Error Handling

ErrorCauseSolution
401
after key rotation
Old key cachedRestart app / clear env cache
403
on campaign activate
Missing
campaigns:update
scope
Regenerate API key with correct scopes
422
duplicate lead
Lead already in workspaceUse
skip_if_in_workspace: true
Campaign
-2
bounce protect
Bounce rate >5%Clean lead list, verify emails before import
Warmup health droppingToo many campaign emails too soonReduce daily_limit, extend warmup period

Resources

Next Steps

For structured debugging, see

instantly-debug-bundle
.