CtrlK
BlogDocsLog inGet started
Tessl Logo

instantly-common-errors

Diagnose and fix Instantly.ai API v2 common errors and exceptions. Use when encountering Instantly errors, debugging failed requests, or troubleshooting campaign/account/lead issues. Trigger with phrases like "instantly error", "instantly 401", "instantly 429", "instantly api failed", "instantly debug", "instantly troubleshoot".

80

Quality

77%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Optimize this skill with Tessl

npx tessl skill review --optimize ./plugins/saas-packs/instantly-pack/skills/instantly-common-errors/SKILL.md
SKILL.md
Quality
Evals
Security

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
400Bad RequestMalformed JSON, invalid field valuesValidate request body against schema
401UnauthorizedInvalid, expired, or revoked API keyRegenerate key in Settings > Integrations
403ForbiddenAPI key missing required scopeCreate key with correct scope (e.g., campaigns:all)
404Not FoundInvalid campaign/lead/account IDVerify resource exists with a GET call first
422Unprocessable EntityBusiness logic violation (duplicate lead, invalid state)Check error body for details
429Too Many RequestsRate limit exceededImplement exponential backoff (see below)
500Internal 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
0DraftNot yet activated
1ActiveCurrently sending
2PausedManually paused
3CompletedAll leads processed
4Running SubsequencesMain sequence done, subsequences active
-1Accounts UnhealthySending accounts have SMTP/IMAP errors
-2Bounce ProtectAuto-paused due to high bounce rate
-99SuspendedAccount-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
1ActiveEligible to receive emails
2PausedManually paused
3CompletedAll sequence steps sent
-1BouncedEmail bounced
-2UnsubscribedLead unsubscribed
-3SkippedSkipped (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 /webhooksWebhook 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/summaryFix 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 rotationOld key cachedRestart app / clear env cache
403 on campaign activateMissing campaigns:update scopeRegenerate API key with correct scopes
422 duplicate leadLead already in workspaceUse skip_if_in_workspace: true
Campaign -2 bounce protectBounce rate >5%Clean lead list, verify emails before import
Warmup health droppingToo many campaign emails too soonReduce daily_limit, extend warmup period

Resources

  • Instantly API v2 Docs
  • Instantly Help Center
  • API Schemas

Next Steps

For structured debugging, see instantly-debug-bundle.

Repository
jeremylongshore/claude-code-plugins-plus-skills
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.