IN

instantly-webhooks-events

Sets up webhook handlers for Instantly email outreach notifications and status updates.

Install

mkdir -p .claude/skills/instantly-webhooks-events && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/2745" && unzip -o skill.zip -d .claude/skills/instantly-webhooks-events && rm skill.zip

Installs to .claude/skills/instantly-webhooks-events

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Implement Instantly.ai webhook event handling with real API v2 event
68 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Create Instantly webhooks via API
  • Handle various Instantly email outreach event types
  • Implement event handlers for specific events
  • List and manage existing webhooks
  • Test webhook delivery
  • Check webhook delivery health

How it works

The skill sets up webhook subscriptions with Instantly to receive real-time event notifications, then processes these events using an Express.js server with defined handlers for different event types.

Inputs & outputs

You give it
Instantly API v2 event notifications via webhooks
You get back
Processed email outreach events, such as replies, bounces, or lead status changes

When to use instantly-webhooks-events

  • Handle email tracking events
  • Build CRM sync pipeline
  • Process open and click data
  • Implement webhook signature validation

About this skill

Instantly Webhooks & Events

Overview

Handle Instantly API v2 webhooks for real-time email outreach event notifications. Instantly fires events when emails are sent, opened, clicked, replied to, or bounced, and when leads change interest status. Webhooks require Hypergrowth plan ($97/mo) or higher. Delivery retries: 3 times within 30 seconds on failure.

Prerequisites

  • Instantly Hypergrowth plan or higher (required for webhooks)
  • API key with all:all or appropriate webhook scopes
  • Public HTTPS endpoint for receiving webhook payloads
  • INSTANTLY_API_KEY environment variable set

Webhook Event Types

Event TypeTriggerKey Payload Fields
email_sentEmail delivered to recipientlead_email, campaign_id, step
email_openedRecipient opens emaillead_email, campaign_id, open_count
email_link_clickedRecipient clicks a linklead_email, campaign_id, link_url
reply_receivedRecipient replieslead_email, campaign_id, reply_text
email_bouncedEmail bounceslead_email, bounce_type, reason
lead_unsubscribedLead unsubscribeslead_email, campaign_id
campaign_completedAll leads in campaign processedcampaign_id, campaign_name
account_errorSending account erroremail, error_type
lead_interestedLead marked interestedlead_email, campaign_id
lead_not_interestedLead marked not interestedlead_email, campaign_id
lead_meeting_bookedMeeting bookedlead_email, campaign_id
lead_meeting_completedMeeting completedlead_email
lead_closedLead closed/wonlead_email
lead_out_of_officeOOO reply detectedlead_email
lead_wrong_personWrong person responselead_email
all_eventsSubscribe to everythingVaries by event

Instructions

Step 1: Create Webhook via API

import { instantly } from "./src/instantly";

async function createWebhook() {
  // Create webhook for specific events
  const webhook = await instantly<{ id: string; name: string }>("/webhooks", {
    method: "POST",
    body: JSON.stringify({
      name: "CRM Sync — Replies & Meetings",
      target_hook_url: "https://api.yourapp.com/webhooks/instantly",
      event_type: "reply_received",
      headers: {
        "X-Webhook-Secret": process.env.INSTANTLY_WEBHOOK_SECRET,
      },
    }),
  });
  console.log(`Webhook created: ${webhook.id}`);

  // Create additional webhooks for other events
  for (const event of ["lead_interested", "lead_meeting_booked", "email_bounced"]) {
    await instantly("/webhooks", {
      method: "POST",
      body: JSON.stringify({
        name: `CRM Sync — ${event}`,
        target_hook_url: "https://api.yourapp.com/webhooks/instantly",
        event_type: event,
        headers: { "X-Webhook-Secret": process.env.INSTANTLY_WEBHOOK_SECRET },
      }),
    });
  }

  // Or subscribe to ALL events with one webhook
  await instantly("/webhooks", {
    method: "POST",
    body: JSON.stringify({
      name: "All Events Monitor",
      target_hook_url: "https://api.yourapp.com/webhooks/instantly/all",
      event_type: "all_events",
      headers: { "X-Webhook-Secret": process.env.INSTANTLY_WEBHOOK_SECRET },
    }),
  });
}

Step 2: Build Event Handler

import express from "express";

const app = express();
app.use(express.json());

app.post("/webhooks/instantly", async (req, res) => {
  // Validate secret
  if (req.headers["x-webhook-secret"] !== process.env.INSTANTLY_WEBHOOK_SECRET) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  // Respond 200 immediately — Instantly retries 3x in 30s on failure
  res.status(200).json({ received: true });

  const { event_type, data } = req.body;
  console.log(`Event: ${event_type}`, JSON.stringify(data).slice(0, 300));

  try {
    await routeEvent(event_type, data);
  } catch (err) {
    console.error(`Failed to process ${event_type}:`, err);
  }
});

async function routeEvent(eventType: string, data: any) {
  switch (eventType) {
    case "reply_received":
      await handleReply(data);
      break;
    case "email_bounced":
      await handleBounce(data);
      break;
    case "lead_interested":
    case "lead_meeting_booked":
    case "lead_closed":
      await handlePositiveOutcome(eventType, data);
      break;
    case "lead_unsubscribed":
      await handleUnsubscribe(data);
      break;
    case "campaign_completed":
      await handleCampaignComplete(data);
      break;
    case "account_error":
      await handleAccountError(data);
      break;
    default:
      console.log(`Unhandled event: ${eventType}`);
  }
}

Step 3: Implement Event Handlers

async function handleReply(data: {
  lead_email: string;
  campaign_id: string;
  reply_text: string;
}) {
  console.log(`Reply from ${data.lead_email} in campaign ${data.campaign_id}`);

  // Sync to CRM
  await crmClient.updateContact(data.lead_email, {
    status: "replied",
    lastReply: data.reply_text,
    lastActivity: new Date(),
  });

  // Notify sales team
  await slackNotify("#sales-replies", {
    text: `Reply from ${data.lead_email}:\n${data.reply_text.slice(0, 500)}`,
  });
}

async function handleBounce(data: {
  lead_email: string;
  bounce_type: string;
  reason: string;
}) {
  console.log(`Bounce: ${data.lead_email} (${data.bounce_type})`);

  if (data.bounce_type === "hard") {
    // Add to global block list
    await instantly("/block-lists-entries", {
      method: "POST",
      body: JSON.stringify({ bl_value: data.lead_email }),
    });
    console.log(`Added ${data.lead_email} to block list`);
  }
}

async function handlePositiveOutcome(
  eventType: string,
  data: { lead_email: string; campaign_id: string }
) {
  const statusMap: Record<string, string> = {
    lead_interested: "interested",
    lead_meeting_booked: "meeting_scheduled",
    lead_closed: "closed_won",
  };

  await crmClient.updateContact(data.lead_email, {
    status: statusMap[eventType] || eventType,
    lastActivity: new Date(),
  });

  if (eventType === "lead_meeting_booked") {
    await slackNotify("#sales-wins", {
      text: `Meeting booked with ${data.lead_email}!`,
    });
  }
}

async function handleUnsubscribe(data: { lead_email: string }) {
  // Add to block list to prevent future outreach across all campaigns
  await instantly("/block-lists-entries", {
    method: "POST",
    body: JSON.stringify({ bl_value: data.lead_email }),
  });
  console.log(`Unsubscribed + blocked: ${data.lead_email}`);
}

async function handleCampaignComplete(data: { campaign_id: string }) {
  // Pull final analytics
  const analytics = await instantly(`/campaigns/analytics?id=${data.campaign_id}`);
  console.log(`Campaign complete:`, analytics);
}

async function handleAccountError(data: { email: string; error_type: string }) {
  console.error(`Account error: ${data.email} — ${data.error_type}`);
  await slackNotify("#ops-alerts", {
    text: `Instantly account error: ${data.email}\nType: ${data.error_type}`,
  });
}

Step 4: Manage Webhooks

// List all webhooks
async function listWebhooks() {
  const webhooks = await instantly<Array<{
    id: string; name: string; event_type: string; target_hook_url: string;
  }>>("/webhooks?limit=50");

  for (const w of webhooks) {
    console.log(`${w.id}: ${w.name} [${w.event_type}] -> ${w.target_hook_url}`);
  }
}

// Test a webhook
async function testWebhook(webhookId: string) {
  await instantly(`/webhooks/${webhookId}/test`, { method: "POST" });
}

// Resume a paused webhook
async function resumeWebhook(webhookId: string) {
  await instantly(`/webhooks/${webhookId}/resume`, { method: "POST" });
}

// Check delivery status
async function checkDeliveryHealth() {
  const summary = await instantly("/webhook-events/summary");
  console.log("Webhook delivery summary:", summary);

  const byDate = await instantly("/webhook-events/summary-by-date");
  console.log("By date:", byDate);
}

// Delete a webhook
async function deleteWebhook(webhookId: string) {
  await instantly(`/webhooks/${webhookId}`, { method: "DELETE" });
}

Key API Endpoints

MethodPathPurpose
POST/webhooksCreate webhook subscription
GET/webhooksList webhooks
PATCH/webhooks/{id}Update webhook
DELETE/webhooks/{id}Delete webhook
POST/webhooks/{id}/testSend test event
POST/webhooks/{id}/resumeResume paused webhook
GET/webhook-eventsList webhook events
GET/webhook-events/summaryDelivery summary

Error Handling

IssueCauseSolution
No events deliveredWebhook not registered or pausedCheck GET /webhooks, resume if paused
Duplicate eventsRetry deliveryDeduplicate by event ID + timestamp
Webhook paused automaticallyToo many delivery failuresFix endpoint, then POST /webhooks/{id}/resume
30s timeoutHandler takes too longReturn 200 immediately, process async
Missing event_typeUsing custom label eventsCheck custom_interest_value field

Resources

Next Steps

For performance optimization, see instantly-performance-tuning.

Prerequisites

Instantly Hypergrowth plan or higherAPI key with appropriate webhook scopesPublic HTTPS endpoint for receiving webhook payloadsINSTANTLY_API_KEY environment variable set

Limitations

  • Instantly retries failed deliveries 3 times within 30 seconds
  • Webhook handler must return 200 immediately to avoid timeouts

How it compares

This skill provides a structured way to securely receive and process Instantly email outreach events in real-time, enabling automated CRM synchronization, unlike polling the API or manual data entry.

Compared to similar skills

instantly-webhooks-events side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
instantly-webhooks-events (this skill)226dCautionIntermediate
telegram-bot-builder1066moReviewIntermediate
n8n-expression-syntax64moNo flagsBeginner
reddit-api34moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jeremylongshore

View all by jeremylongshore

analyzing-logs

jeremylongshore

Analyze application logs to detect performance issues, identify error patterns, and improve stability by extracting key insights.

14123

ollama-setup

jeremylongshore

Configure auto-configure Ollama when user needs local LLM deployment, free AI alternatives, or wants to eliminate hosted API costs. Trigger phrases: "install ollama", "local AI", "free LLM", "self-hosted AI", "replace OpenAI", "no API costs". Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

1167

backtesting-trading-strategies

jeremylongshore

Backtest crypto and traditional trading strategies against historical data. Calculates performance metrics (Sharpe, Sortino, max drawdown), generates equity curves, and optimizes strategy parameters. Use when user wants to test a trading strategy, validate signals, or compare approaches. Trigger with phrases like "backtest strategy", "test trading strategy", "historical performance", "simulate trades", "optimize parameters", or "validate signals".

1071

generating-database-seed-data

jeremylongshore

Process this skill enables AI assistant to generate realistic test data and database seed scripts for development and testing environments. it uses faker libraries to create realistic data, maintains relational integrity, and allows configurable data volumes. u... Use when working with databases or data models. Trigger with phrases like 'database', 'query', or 'schema'.

1033

cursor-codebase-indexing

jeremylongshore

Execute set up and optimize Cursor codebase indexing. Triggers on "cursor index setup", "codebase indexing", "index codebase", "cursor semantic search". Use when working with cursor codebase indexing functionality. Trigger with phrases like "cursor codebase indexing", "cursor indexing", "cursor".

885

testing-mobile-apps

jeremylongshore

Execute mobile app testing on iOS and Android devices/simulators. Use when performing specialized testing. Trigger with phrases like "test mobile app", "run iOS tests", or "validate Android functionality".

810

You might also like

Search skills

Search the agent skills registry