Updated February 23, 2026
TL;DR: Webhooks deliver email event data (sent, delivered, opened, clicked, replied, bounced) to your server in real time. This cuts out the waste and lag of API polling. This guide walks through configuring Instantly webhooks, parsing JSON payloads for critical events, and securing your endpoints. Use webhooks to auto-update your CRM, trigger Slack alerts for hot leads, and maintain list hygiene without manual CSV exports. Webhooks are available on Hypergrowth and above, require an HTTPS endpoint, and push event data as events occur.
Growth teams waste hours polling APIs for data that should push itself. If you run campaigns across dozens of sending accounts and need to route replies, classify bounces, or feed engagement signals into a dashboard, you need real-time event handling. It's the difference between catching a warm lead in five minutes and missing them entirely. Webhooks flip the script. Instead of asking "anything new?" every 60 seconds, your server listens and reacts instantly when a prospect replies or an email bounces.
Instantly's webhook system supports granular event types built specifically for cold outreach workflows. Events range from reply_received and email_bounced to sales-specific triggers like lead_meeting_booked and custom labels.
This guide covers the setup flow, JSON payload structures for key events, idempotency and security best practices, and real-world integration patterns with CRMs and automation tools.

Why webhooks beat polling for email analytics
Polling means repeatedly asking an API "do you have new data?" even when the answer is no. Webhooks push updates the moment an event fires, which saves bandwidth, reduces server load, and eliminates lag.
Efficiency wins
Polling drains resources by checking for updates even when nothing has changed.
How polling wastes cycles:
- Each request consumes API rate limits and server cycles
- You pay processing cost for empty checks
- Bandwidth costs scale with polling frequency, not event volume
How webhooks save resources:
- Only fire when something happens
- You pay processing cost exclusively for actionable events
- Zero bandwidth wasted on "no new data" responses
Real-time value for sales teams
A reply event matters most in the first few minutes. Webhooks push event data as it happens. Polling at 5-minute intervals means your sales team misses the window.
Example timing comparison:
| Method | Avg. delay | Best case | Worst case |
|---|---|---|---|
| Webhook push | Seconds | Instant | Few seconds |
| Poll every 5 min | 2.5 minutes | 0 seconds | 5 minutes |
| Poll every hour | 30 minutes | 0 seconds | 60 minutes |
If you poll every hour and a lead replies 2 minutes after your last check, you wait 58 more minutes while that lead moves on. Webhooks close that gap.
Data integrity at scale
You can skip events with polling if your fetch window is too narrow or if you hit rate limits during high-volume campaigns. Webhooks use HTTP POST to deliver each event in sequence, reducing the risk of missing critical signals like bounces or spam complaints.
One agency operator highlighted how:
"Instantly just works and does the basics exceptionally well! Easy integration with tools like HubSpot, Clay, Airtable, and n8n" - Chico C on G2.

Core email events to track for granular visibility
We support these webhook event types in Instantly:
Delivery events
- email_sent: Confirmation the message left your sending infrastructure
- email_opened: Prospect loaded the email (pixel-based tracking)
- link_clicked: Prospect clicked a tracked link in the message
When to use: Track delivery events to confirm campaign throughput and diagnose sending account issues.
Engagement and response
- reply_received: The money event. A real human replied to your sequence.
- auto_reply_received: Out-of-office or automated response detected
When to use: Route hot leads to sales reps immediately and stop sequences to prevent over-mailing engaged prospects.
Risk and hygiene
- email_bounced: Delivery failure that requires list action
- lead_unsubscribed: Prospect opted out. Remove from future sends.
- account_error: Sending account issue (credentials, suspension, etc.)
When to use: Protect sender reputation by removing invalid addresses and monitoring account health in real time.
Sales-specific triggers
- lead_interested, lead_not_interested, lead_neutral: Manual or AI classification
- lead_meeting_booked, lead_meeting_completed: Calendar events tied to outreach
- lead_closed, lead_out_of_office, lead_wrong_person: Workflow states
- campaign_completed: All steps finished for a given lead
When to use: Automate pipeline stage updates and CRM task creation without manual data entry.
Custom labels
Any workspace-specific label fires as its own event type. Set your subscription to all_events to catch everything, including custom tags.
For detailed campaign configuration, check Campaign Options in the help center to toggle tracking for opens, clicks, and delivery. One user called out how
"Deliverability tools that actually move the needle: warmup, inbox rotation, and smart sending windows help us land in Primary instead of Promotions/Spam." - Anthony V on G2
Step-by-step: Setting up webhooks in Instantly
We include webhooks on Hypergrowth plan and above ($97/mo or $77.60/mo annually). Growth plan gives you API access but not webhooks.
Prerequisites
Before you start:
- Hypergrowth plan or above required
- Basic HTTP/JSON knowledge
- Access to a server or automation platform (Make, Zapier, n8n)
- HTTPS endpoint URL (production) or ngrok tunnel (testing)
- 15 to 30 minutes for initial setup
Setup steps
1. Navigate to webhook settings
Go to Settings → Integrations → Webhooks inside Instantly. You can also reach this from the Integrations tab in your workspace. Detailed navigation steps walk through the UI.
2. Add a new webhook
Click Add Webhook. You'll see three required fields:
- Destination URL: The HTTPS endpoint on your server, for example
https://yourdomain.com/webhooks/instantly. We make HTTP POST requests to this URL. - Custom headers: Attach authentication tokens or context metadata. Use
Authorization: Bearer YOUR_SECRETif your endpoint requires it. - Event type: Choose specific events (
reply_received,email_bounced) or selectall_eventsto receive everything.
3. Select campaign scope
Pick which campaign(s) should send events to this webhook. You can create separate webhooks per campaign or reuse one endpoint with event filtering logic on your side.
4. Test the connection
Click Add Webhook and send a test event. Our webhook activity monitoring shows delivery status, retry attempts, and error messages. Check your server logs to confirm the test payload arrived.
For automation workflows, watch how integration is done with n8n for event-driven sequences.
JSON payload reference by event type
All our webhook events share a base schema:
{
"timestamp": "2026-01-28T14:32:11.042Z",
"event_type": "reply_received",
"workspace": "workspace-uuid-here",
"campaign_id": "campaign-uuid-here",
"campaign_name": "Q1 SaaS Outreach",
"lead_email": "prospect@company.com",
"email_account": "sender@yourdomain.com"
}
Key fields explained
- timestamp (ISO 8601): When the event occurred
- event_type: One of the event names listed above
- campaign_id and campaign_name: Campaign context for routing
- lead_email: Prospect address (when applicable)
- email_account: Which sending inbox handled this message
Additional fields depend on the event type. The documentation does not publish exhaustive payload examples for every event, so test in a sandbox environment and log the full JSON structure for your specific events.
The Reply payload
The reply_received event is your highest-value signal. Map the reply back to your database using campaign_id and lead_email, then parse the message body for sentiment or keywords. Test your webhook in a sandbox campaign and log the full JSON to confirm which fields contain reply text. Feed this field into an NLP library or OpenAI's API to classify as "Interested," "Not Interested," or "Out of Office."
This prevents double-counting reply rates, creating duplicate CRM tasks, or sending redundant Slack alerts when we retry a failed delivery.
One developer walked through parsing replies with AI sentiment analysis using Instantly webhooks, Make, and GPT-4 to auto-generate personalized proposals.
The Bounce payload
The email_bounced event protects your sender reputation. Check the payload for bounce classification data and trigger immediate list hygiene:
Hard bounce handling:
- Invalid address or domain does not exist
- Remove from your database immediately
- Mark as "Do Not Contact"
Soft bounce handling:
- Temporary issue (mailbox full, server down)
- Most ESP guidelines recommend allowing email providers 72 hours to retry delivery
- If the soft bounce persists beyond 3 to 5 days, convert to hard bounce status
Log diagnostic data for pattern analysis, especially if you see clusters of bounces from one domain or sending account.
The Open and Click payloads
email_opened and link_clicked events pass timestamp for tracking. Opens use pixel tracking, which means some privacy-focused clients (Apple Mail, Outlook with images off) may not fire the event. Treat opens as directional signals, not absolute counts. Clicks are more reliable, assuming you use Instantly's link tracking.
Advanced implementation: Reply tracking and sentiment
Handling replies requires mapping the webhook event back to your original send record.
Linking strategy
- Store the
campaign_id,lead_email, andemail_accountwhen you launch a campaign - When the
reply_receivedevent arrives, match oncampaign_id+lead_email - Pull the full reply text from the payload (test to identify the exact field name in your environment)
Sentiment workflow
- Extract the reply body text from the webhook payload
- POST to OpenAI's chat completions endpoint with a prompt like: "Classify this cold email reply as Interested, Not Interested, or Out of Office: [reply text]"
- Update your CRM with the classification and trigger the next action (book meeting, remove from sequence, send to sales)
"the AI reply tool is incredibly valuable to me as it automates all email replies, preventing the need to go back and forth, which is a huge timesaver" -faisal K. on G2
For custom sentiment logic, webhooks give you full control over the analysis pipeline.
Security, idempotency, and error handling
Security: Verify webhook requests
The Instantly documentation supports custom headers for authentication. We recommend this workflow for production security:
- Generate a random token, for example
Authorization: Bearer abc123xyz - Add it in the webhook configuration UI under Custom Headers
- On your server, reject any request without that header
- Enforce HTTPS and allowlist our IP ranges (contact support for the current list)
Most platforms use HMAC-SHA256 to hash the payload with a shared secret.
Idempotency: Handle duplicate events
Webhooks can deliver the same event twice if your server times out or returns a 5xx error. Store a unique identifier from each payload in your database. Before processing, check if you've seen this event before:
if event_id_exists(payload['id']):
return 200 # Already processed, skip
This prevents creating duplicate tasks, double-counting opens, or sending redundant Slack alerts.
Retries and error codes
According to the Instantly webhook guide, we retry failed webhook deliveries up to three times within 30 seconds. Our webhook schema includes retry_count, will_retry, timestamp_next_retry, and retry_group_id fields to track delivery attempts.
Your server must:
- Return
200 OKquickly to acknowledge receipt - Process the event asynchronously if it takes longer (queue the payload, return 200 immediately, then process in a worker)
- Log 4xx/5xx errors and monitor your webhook activity dashboard for failures
"Native/Webhook options to push replies and positives into our CRM and Slack without custom plumbing." - Anthony V on G2
Real-world integration examples
Scenario A: CRM sync on reply
Goal: When a lead replies, create a task in HubSpot for the sales rep.
Flow:
- Webhook fires
reply_receivedto your server - Extract
lead_email,campaign_name, and reply body from payload - POST to HubSpot Tasks API with subject "Follow up: [Campaign Name]" and notes containing the reply
- Tag the contact record with "Hot Lead"
For step-by-step Zapier examples, see create a contact in HubSpot when marked as Interested or add a lead to Close CRM.
Scenario B: Slack alert for hot leads
Goal: Ping your sales channel when a reply comes in.
Flow:
- Webhook fires
reply_receivedand your server parses payload - Format a Slack message: "🔥 New reply from [Company] in campaign [Name]. [View in Instantly](link)"
- POST to Slack Incoming Webhook
Scenario C: Auto-hygiene on bounce
Goal: Remove hard bounces from your database immediately.
Flow:
- Webhook fires
email_bouncedand you parse the payload - Check for bounce classification data (test your environment to identify available fields)
- If identified as permanent failure, DELETE contact from your leads table
- Log the bounce reason and domain for pattern analysis
This protects your sender reputation by preventing repeat sends to invalid addresses. Check out the full cold email deliverability guide for context on bounce management.
Scenario D: Meeting booked pipeline
Goal: Update your CRM when a lead books a meeting via your calendar link.
Flow:
- Webhook fires
lead_meeting_bookedand you parse payload forlead_emailand meeting details - Create a deal in Pipedrive or update opportunity stage in Salesforce
- Remove lead from active outreach sequences
For more on Pipedrive integration, see the help docs.
Troubleshooting common webhook issues
Webhooks not received
Causes:
- URL is not publicly accessible (e.g.,
localhostwithout a tunnel) - Firewall or network blocking our requests
- Endpoint path has a typo
Fix:
Test your endpoint with curl or Postman. For local development, use ngrok to expose localhost over HTTPS. Run ngrok http 3000, copy the public URL, and paste it into our webhook config. ngrok's request inspector at http://127.0.0.1:4040 lets you replay requests during debugging.
Payloads are empty or missing expected fields
Causes:
- Event type selection in Instantly settings does not match what you're listening for
- Campaign tracking options (opens, clicks) are disabled
Fix:
Double-check Campaign Options to ensure delivery and engagement tracking is on. Review your webhook configuration for the correct event types.
Duplicate entries in your database
Causes:
- Your server returned a 5xx error or timed out, so we retried
- No idempotency check on a unique event identifier
Fix:
Store a unique identifier from each webhook payload before processing. If it exists, return 200 immediately without creating new records.
Server timeout
Causes:
- Webhook handler does heavy processing (API calls, database writes) in the request thread
Fix:
Queue the payload in Redis or RabbitMQ, return 200 quickly, then process asynchronously in a worker. This keeps our retry logic clean.
Sample webhook receiver code
Here's a minimal Node.js Express server that handles Instantly webhooks. This example shows idempotency checks, event routing, and proper 200 OK responses:
const express = require('express');
const app = express();
app.use(express.json());
// In-memory idempotency cache (use Redis in production)
const processedEvents = new Set();
app.post('/webhooks/instantly', (req, res) => {
try {
const event = req.body;
const eventId = event.id || `${event.timestamp}-${event.event_type}-${event.lead_email}`;
// Idempotency check
if (processedEvents.has(eventId)) {
console.log(`Duplicate event ${eventId}, skipping`);
return res.status(200).json({ received: true });
}
processedEvents.add(eventId);
console.log(`Event: ${event.event_type} | Campaign: ${event.campaign_name}`);
// Route by event type
switch(event.event_type) {
case 'reply_received':
console.log(`Reply from ${event.lead_email}`);
// TODO: Parse body, classify sentiment, update CRM
break;
case 'email_bounced':
console.log(`Bounce: ${event.lead_email}`);
// TODO: Remove from database
break;
case 'lead_meeting_booked':
console.log(`Meeting booked: ${event.lead_email}`);
// TODO: Create CRM deal
break;
}
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => console.log('Webhook server on port 3000'));
Python Flask version:
from flask import Flask, request, jsonify
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
processed_events = set()
@app.route('/webhooks/instantly', methods=['POST'])
def instantly_webhook():
try:
event = request.json
event_id = event.get('id') or f"{event.get('timestamp')}-{event.get('event_type')}-{event.get('lead_email')}"
if event_id in processed_events:
logging.info(f"Duplicate event {event_id}, skipping")
return jsonify({'received': True}), 200
processed_events.add(event_id)
event_type = event.get('event_type')
logging.info(f"Event: {event_type} | Campaign: {event.get('campaign_name')}")
if event_type == 'reply_received':
logging.info(f"Reply from {event.get('lead_email')}")
# TODO: Parse body, classify sentiment, update CRM
elif event_type == 'email_bounced':
logging.info(f"Bounce: {event.get('lead_email')}")
# TODO: Remove from database
return jsonify({'received': True}), 200
except Exception as e:
logging.error(f"Webhook error: {str(e)}")
return jsonify({'error': 'Internal server error'}), 500
if __name__ == '__main__':
app.run(port=3000)
For no-code workflows, check out the Pabbly integration guide or explore n8n workflows for cold email automation.
Testing webhooks on localhost with ngrok
Production webhooks require an HTTPS endpoint. For local testing, ngrok creates a secure tunnel from the internet to your dev machine.
ngrok setup steps
- Install ngrok: Download from ngrok.com and sign up for a free account (optional but recommended).
- Copy the public URL: You'll see
Forwarding: https://abc123.ngrok.io → http://localhost:3000. This means all requests to the ngrok URL will be forwarded to your local machine. - Configure in Instantly: Paste
https://abc123.ngrok.io/webhooks/instantlyinto the webhook URL field in our Integrations tab. - Inspect traffic: Access a request inspector in a browser at
http://127.0.0.1:4040, where you can see (and even replay) any HTTP requests that went through.
Open the tunnel:
ngrok http 3000
Start your local server:
# Example: Node.js server on port 3000
node server.js
Cloudflare Tunnel is a production-grade alternative with persistent URLs and no session limits.
Start listening: Next steps for growth teams
Start with reply_received and email_bounced events. They offer the highest ROI for automation. Reply events feed your CRM and alert sales reps in seconds. Bounce events protect sender reputation by removing bad addresses immediately.
Once those workflows stabilize, layer in lead_interested and lead_meeting_booked to automate pipeline stages. Custom labels let you build bespoke routing logic without touching code. For teams running multi-channel campaigns, our API documentation covers advanced filtering and the full event catalog.
"Instantly for email outreach and lead generation...handles almost everything in one place: finding leads, sending campaigns, warming up inboxes...removes a lot of technical friction and saves a huge amount of time" - Piyush Mohanty on Trustpilot
Ready to cut polling lag and build event-driven workflows? Try Instantly free and configure your first reply_received webhook in the Integrations tab. For real-time reply automation, watch how to instantly respond to emails with an n8n AI agent.
Frequently asked questions
What is the difference between a webhook and an API?
An API is a request-response model where you pull data by asking for it. A webhook pushes data to your server automatically when an event occurs.
Does Instantly charge for webhook events?
No. We include webhooks in Hypergrowth and above ($97/mo or $77.60/mo annual) with no per-event fees.
How do I test webhooks locally?
Use ngrok to tunnel your localhost over HTTPS. Run ngrok http 3000, copy the public URL, and paste it into our webhook configuration.
Can I send webhooks to multiple URLs?
Yes. Create separate webhook configurations for each endpoint to route different event types or campaigns to different URLs.
What happens if my server is down when an event fires?
We retry failed webhook deliveries up to three times within 30 seconds. Check your webhook activity dashboard for delivery status.
Key terms glossary
Payload: The JSON data Instantly POSTs to your webhook endpoint, containing event type, timestamp, campaign context, and event-specific fields.
Endpoint: The HTTPS URL on your server that receives webhook requests. Must return 200 OK quickly to acknowledge receipt.
Idempotency: The property that processing the same event twice produces the same result as processing it once. Prevents duplicate CRM entries or alerts.
HMAC: Hash-based Message Authentication Code, used to sign webhook payloads and verify they came from a trusted source.
Hard bounce: Permanent delivery failure (invalid address or domain does not exist). Remove from your list immediately.
Soft bounce: Temporary failure (mailbox full or server down). Allow 72 hours for provider retry, then convert to hard bounce if it persists.