Skip to main content

What are Webhooks?

Webhooks allow you to receive real-time notifications when events occur in your SmartLead campaigns. Instead of polling the API, SmartLead will send HTTP POST requests to your server when events happen.

Use Cases

CRM Integration

Update your CRM when leads reply or book meetings

Lead Scoring

Score leads based on engagement (opens, clicks)

Notifications

Get Slack/Email alerts for important replies

Analytics

Send data to your analytics platform

Available Events

EventDescriptionWhen Triggered
EMAIL_SENTEmail sent to leadAfter successful delivery
EMAIL_OPENEDLead opened emailWhen tracking pixel loads
EMAIL_CLICKEDLead clicked linkWhen tracked link is clicked
EMAIL_REPLIEDLead replied to emailWhen reply is received
EMAIL_BOUNCEDEmail bouncedWhen email fails to deliver
EMAIL_UNSUBSCRIBEDLead unsubscribedWhen unsubscribe link clicked

Webhook Payload Format

All webhook events follow this structure:
{
  "event": "EMAIL_REPLIED",
  "timestamp": "2024-01-15T10:30:00Z",
  "campaign_id": 123,
  "campaign_name": "Cold Outreach Q1",
  "lead_id": 789,
  "email_account_id": 456,
  "lead": {
    "email": "lead@example.com",
    "first_name": "Jane",
    "last_name": "Doe",
    "company_name": "Acme Corp",
    "custom_fields": {
      "job_title": "CEO"
    }
  },
  "sequence_number": 1,
  "email": {
    "subject": "Quick question",
    "message_id": "abc123@smartlead.ai"
  },
  "reply": {
    "subject": "Re: Quick question",
    "body": "Thanks for reaching out...",
    "received_at": "2024-01-15T10:30:00Z"
  }
}

Setting Up Webhooks

1

Create Webhook Endpoint

Set up an HTTPS endpoint on your server that accepts POST requests
Python
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    data = request.json
    event = data['event']
    
    if event == 'EMAIL_REPLIED':
        # Handle reply
        lead_email = data['lead']['email']
        reply_body = data['reply']['body']
        # Your logic here
        
    return {'status': 'success'}, 200
2

Register Webhook in SmartLead

Use the API to register your webhook URL
cURL
curl -X POST "https://server.smartlead.ai/api/v1/webhook/create?api_key=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CRM Integration",
    "webhook_url": "https://your-server.com/webhook",
    "email_campaign_id": 123,
    "association_type": 3,
    "event_type_map": {
      "EMAIL_REPLIED": true,
      "EMAIL_OPENED": true
    }
  }'
3

Test Your Webhook

SmartLead will send test events when you save the webhook
4

Go Live

Activate your campaign and start receiving events

Webhook Association Types

Webhooks can be associated with:
  • User Level (association_type: 1): Receive events from all campaigns
  • Client Level (association_type: 2): Events for specific client’s campaigns
  • Campaign Level (association_type: 3): Events from a single campaign

Event Examples

EMAIL_SENT

{
  "event": "EMAIL_SENT",
  "timestamp": "2024-01-15T09:00:00Z",
  "campaign_id": 123,
  "lead_id": 789,
  "email_account_id": 456,
  "sequence_number": 1,
  "lead": {
    "email": "lead@example.com",
    "first_name": "Jane"
  }
}

EMAIL_OPENED

{
  "event": "EMAIL_OPENED",
  "timestamp": "2024-01-15T10:30:00Z",
  "campaign_id": 123,
  "lead_id": 789,
  "sequence_number": 1,
  "opened_count": 3,
  "first_opened_at": "2024-01-15T10:30:00Z",
  "last_opened_at": "2024-01-15T14:20:00Z"
}

EMAIL_REPLIED

{
  "event": "EMAIL_REPLIED",
  "timestamp": "2024-01-15T11:00:00Z",
  "campaign_id": 123,
  "lead_id": 789,
  "email_account_id": 456,
  "sequence_number": 1,
  "reply": {
    "subject": "Re: Quick question",
    "body": "Thanks for reaching out. I'm interested...",
    "received_at": "2024-01-15T11:00:00Z",
    "message_id": "reply-abc123"
  },
  "lead": {
    "email": "lead@example.com",
    "first_name": "Jane",
    "last_name": "Doe"
  }
}

EMAIL_CLICKED

{
  "event": "EMAIL_CLICKED",
  "timestamp": "2024-01-15T10:45:00Z",
  "campaign_id": 123,
  "lead_id": 789,
  "sequence_number": 1,
  "link": {
    "url": "https://example.com/demo",
    "clicked_at": "2024-01-15T10:45:00Z"
  }
}

Webhook Security

Verify Webhook Origin

Always verify webhooks come from SmartLead:
Python
import hmac
import hashlib

def verify_webhook(payload, signature, secret):
    """Verify webhook signature"""
    expected = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(expected, signature)
Always use HTTPS for your webhook endpoint to ensure data is encrypted in transit.

Best Practices

  1. Return 200 Quickly: Process webhooks asynchronously
  2. Implement Retry Logic: Handle temporary failures
  3. Validate Payload: Check all required fields exist
  4. Log Everything: Keep webhook logs for debugging
  5. Use Idempotency: Handle duplicate events gracefully

Example Implementations

Node.js/Express

const express = require('express');
const app = express();

app.post('/webhook', express.json(), (req, res) => {
  const { event, lead, reply } = req.body;
  
  // Quickly acknowledge receipt
  res.status(200).json({ status: 'received' });
  
  // Process asynchronously
  process.nextTick(() => {
    switch(event) {
      case 'EMAIL_REPLIED':
        console.log(`Reply from ${lead.email}: ${reply.body}`);
        // Update your CRM, send notifications, etc.
        break;
      case 'EMAIL_OPENED':
        console.log(`${lead.email} opened email`);
        break;
    }
  });
});

app.listen(3000);

Python/Flask

from flask import Flask, request
import logging

app = Flask(__name__)
logger = logging.getLogger(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    data = request.json
    
    # Log the event
    logger.info(f"Received {data['event']} event")
    
    # Quick response
    response = {'status': 'received'}
    
    # Process asynchronously (use Celery, etc.)
    process_webhook_async(data)
    
    return response, 200

def process_webhook_async(data):
    event = data['event']
    
    if event == 'EMAIL_REPLIED':
        # Update CRM
        update_crm_contact(
            email=data['lead']['email'],
            status='Replied'
        )
        
        # Send Slack notification
        send_slack_notification(
            f"New reply from {data['lead']['first_name']}!"
        )

Webhook Management

Create Webhook

cURL
curl -X POST "https://server.smartlead.ai/api/v1/webhook/create?api_key=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Webhook",
    "webhook_url": "https://your-server.com/webhook",
    "email_campaign_id": 123,
    "association_type": 3,
    "event_type_map": {
      "EMAIL_SENT": true,
      "EMAIL_OPENED": true,
      "EMAIL_CLICKED": true,
      "EMAIL_REPLIED": true,
      "EMAIL_BOUNCED": true
    }
  }'

Get Webhook Details

cURL
curl -X GET "https://server.smartlead.ai/api/v1/webhook/123?api_key=YOUR_API_KEY"

Delete Webhook

cURL
curl -X DELETE "https://server.smartlead.ai/api/v1/webhook/delete/123?api_key=YOUR_API_KEY"

Retry Logic

SmartLead will retry failed webhook deliveries:
  • 1st retry: After 1 minute
  • 2nd retry: After 5 minutes
  • 3rd retry: After 15 minutes
  • 4th retry: After 1 hour
  • 5th retry: After 6 hours
After 5 failed attempts, the webhook will be disabled.

Debugging Webhooks

Common Issues

Check:
  • URL is publicly accessible
  • HTTPS is properly configured
  • Firewall allows SmartLead IPs
  • Server is returning 200 status code
Solution: Use the timestamp field to order events, not arrival time
Solution: Use idempotency keys (lead_id + event + timestamp)
Reason: Too many failures (5+ consecutive errors) Solution: Fix your endpoint and re-enable webhook

Test Your Webhook

Use a webhook testing service:

Integration Examples

Update HubSpot

def handle_reply_webhook(data):
    """Update HubSpot when lead replies"""
    if data['event'] == 'EMAIL_REPLIED':
        hubspot_contact_id = find_contact_by_email(
            data['lead']['email']
        )
        
        if hubspot_contact_id:
            update_hubspot_contact(
                contact_id=hubspot_contact_id,
                properties={
                    'lead_status': 'Engaged',
                    'last_activity': data['timestamp'],
                    'reply_text': data['reply']['body']
                }
            )

Send Slack Notification

import requests

def send_slack_notification(data):
    """Send Slack alert for replies"""
    if data['event'] == 'EMAIL_REPLIED':
        slack_webhook_url = 'YOUR_SLACK_WEBHOOK_URL'
        
        message = {
            'text': f"🎉 New reply from {data['lead']['first_name']}!",
            'attachments': [{
                'color': 'good',
                'fields': [
                    {
                        'title': 'Lead',
                        'value': data['lead']['email'],
                        'short': True
                    },
                    {
                        'title': 'Campaign',
                        'value': data['campaign_name'],
                        'short': True
                    },
                    {
                        'title': 'Reply',
                        'value': data['reply']['body'][:100] + '...',
                        'short': False
                    }
                ]
            }]
        }
        
        requests.post(slack_webhook_url, json=message)

Rate Limiting

Webhook deliveries are not subject to API rate limits. However, ensure your server can handle:
  • Burst traffic: Many events arriving simultaneously
  • Sustained load: Continuous event stream during active campaigns
Use a queue system (Redis, RabbitMQ) to handle webhook events asynchronously.