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.zipInstalls 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 eventKey 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
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:allor appropriate webhook scopes - Public HTTPS endpoint for receiving webhook payloads
INSTANTLY_API_KEYenvironment variable set
Webhook Event Types
| Event Type | Trigger | Key Payload Fields |
|---|---|---|
email_sent | Email delivered to recipient | lead_email, campaign_id, step |
email_opened | Recipient opens email | lead_email, campaign_id, open_count |
email_link_clicked | Recipient clicks a link | lead_email, campaign_id, link_url |
reply_received | Recipient replies | lead_email, campaign_id, reply_text |
email_bounced | Email bounces | lead_email, bounce_type, reason |
lead_unsubscribed | Lead unsubscribes | lead_email, campaign_id |
campaign_completed | All leads in campaign processed | campaign_id, campaign_name |
account_error | Sending account error | email, error_type |
lead_interested | Lead marked interested | lead_email, campaign_id |
lead_not_interested | Lead marked not interested | lead_email, campaign_id |
lead_meeting_booked | Meeting booked | lead_email, campaign_id |
lead_meeting_completed | Meeting completed | lead_email |
lead_closed | Lead closed/won | lead_email |
lead_out_of_office | OOO reply detected | lead_email |
lead_wrong_person | Wrong person response | lead_email |
all_events | Subscribe to everything | Varies 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
| Method | Path | Purpose |
|---|---|---|
POST | /webhooks | Create webhook subscription |
GET | /webhooks | List webhooks |
PATCH | /webhooks/{id} | Update webhook |
DELETE | /webhooks/{id} | Delete webhook |
POST | /webhooks/{id}/test | Send test event |
POST | /webhooks/{id}/resume | Resume paused webhook |
GET | /webhook-events | List webhook events |
GET | /webhook-events/summary | Delivery summary |
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| No events delivered | Webhook not registered or paused | Check GET /webhooks, resume if paused |
| Duplicate events | Retry delivery | Deduplicate by event ID + timestamp |
| Webhook paused automatically | Too many delivery failures | Fix endpoint, then POST /webhooks/{id}/resume |
| 30s timeout | Handler takes too long | Return 200 immediately, process async |
| Missing event_type | Using custom label events | Check custom_interest_value field |
Resources
- Instantly Webhook API
- Instantly Webhook Events
- Instantly Blog: Webhooks Guide
Next Steps
For performance optimization, see instantly-performance-tuning.
Prerequisites
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.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| instantly-webhooks-events (this skill) | 2 | 27d | Caution | Intermediate |
| telegram-bot-builder | 106 | 6mo | Review | Intermediate |
| n8n-expression-syntax | 6 | 4mo | No flags | Beginner |
| reddit-api | 3 | 4mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
telegram-bot-builder
davila7
Expert in building Telegram bots that solve real problems - from simple automation to complex AI-powered bots. Covers bot architecture, the Telegram Bot API, user experience, monetization strategies, and scaling bots to thousands of users. Use when: telegram bot, bot api, telegram automation, chat bot telegram, tg bot.
n8n-expression-syntax
czlonkowski
Validate n8n expression syntax and fix common errors. Use when writing n8n expressions, using {{}} syntax, accessing $json/$node variables, troubleshooting expression errors, or working with webhook data in workflows.
reddit-api
alinaqi
Reddit API with PRAW (Python) and Snoowrap (Node.js)
mcporter
openclaw
Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.
calcom-api
calcom
Interact with the Cal.com API v2 to manage scheduling, bookings, event types, availability, and calendars. Use this skill when building integrations that need to create or manage bookings, check availability, configure event types, or sync calendars with Cal.com's scheduling infrastructure.
linear-webhooks-events
jeremylongshore
Configure and handle Linear webhooks for real-time event processing. Use when setting up webhooks, handling Linear events, or building real-time integrations. Trigger with phrases like "linear webhooks", "linear events", "linear real-time", "handle linear webhook", "linear webhook setup".