Skip to main content
This guide walks you through setting up webhook subscriptions to receive real-time event notifications.

Creating a Webhook Subscription

Via Dashboard

  1. Log in to your SendPilot account
  2. Navigate to IntegrationsWebhooks
  3. Click Add Webhook
  4. Configure your webhook:
    • Name: A descriptive name for this webhook
    • URL: Your HTTPS endpoint that will receive events
    • Events: Select which events to subscribe to
  5. Click Create
  6. Copy your webhook secret for signature verification

Via API

You can also manage webhooks programmatically through the SendPilot dashboard API.

Endpoint Requirements

Your webhook endpoint must:
  • Accept HTTP POST requests
  • Use HTTPS (HTTP is not supported for security)
  • Respond with a 2xx status code within 30 seconds
  • Accept JSON content type

Example Endpoint (Node.js/Express)

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

// Parse raw body for signature verification
app.use('/webhooks', express.raw({ type: 'application/json' }));

app.post('/webhooks/sendpilot', (req, res) => {
  // Verify signature
  const signature = req.headers['webhook-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  
  if (!verifySignature(req.body, signature, secret)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Parse the event
  const event = JSON.parse(req.body);
  
  // Handle the event asynchronously
  handleEvent(event).catch(console.error);
  
  // Respond immediately
  res.status(200).send('OK');
});

function verifySignature(payload, signature, secret) {
  const parts = signature.split(',');
  const timestamp = parts.find(p => p.startsWith('t=')).slice(2);
  const providedSig = parts.find(p => p.startsWith('s=')).slice(2);
  
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(providedSig),
    Buffer.from(expectedSig)
  );
}

async function handleEvent(event) {
  console.log(`Received ${event.eventType}:`, event.data);
  
  switch (event.eventType) {
    case 'message.received':
      // Handle reply from lead
      await notifySalesTeam(event.data);
      break;
    case 'connection.accepted':
      // Handle new connection
      await syncToCRM(event.data);
      break;
    // ... handle other events
  }
}

app.listen(3000);

Example Endpoint (Python/Flask)

from flask import Flask, request, abort
import hmac
import hashlib
import json

app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET')

@app.route('/webhooks/sendpilot', methods=['POST'])
def handle_webhook():
    # Verify signature
    signature = request.headers.get('Webhook-Signature')
    if not verify_signature(request.data, signature, WEBHOOK_SECRET):
        abort(401)
    
    # Parse event
    event = request.get_json()
    
    # Handle asynchronously (use Celery, RQ, etc. in production)
    handle_event(event)
    
    return 'OK', 200

def verify_signature(payload, signature, secret):
    parts = dict(p.split('=') for p in signature.split(','))
    timestamp = parts['t']
    provided_sig = parts['s']
    
    signed_payload = f"{timestamp}.{payload.decode()}"
    expected_sig = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(provided_sig, expected_sig)

def handle_event(event):
    event_type = event['eventType']
    data = event['data']
    
    if event_type == 'message.received':
        notify_sales_team(data)
    elif event_type == 'connection.accepted':
        sync_to_crm(data)
    # ... handle other events

if __name__ == '__main__':
    app.run(port=3000)

Testing Webhooks

Using the Test Feature

  1. Go to IntegrationsWebhooks in the dashboard
  2. Find your webhook subscription
  3. Click Send Test Event
  4. Select an event type
  5. Check your endpoint for the test payload

Local Development

For local development, use a tunnel service to expose your local server:
# Using ngrok
ngrok http 3000

# Your webhook URL will be:
# https://abc123.ngrok.io/webhooks/sendpilot
Remember to update your webhook URL to your production endpoint before going live.

Managing Subscriptions

Update Events

To change which events trigger your webhook:
  1. Go to IntegrationsWebhooks
  2. Click on the webhook to edit
  3. Select or deselect event types
  4. Save changes

Rotate Secrets

To rotate your webhook secret:
  1. Go to IntegrationsWebhooks
  2. Click on the webhook to edit
  3. Click Rotate Secret
  4. Update your server with the new secret
  5. Confirm the rotation
After rotation, both the old and new secrets will work for 24 hours to allow for seamless updates.

Disable/Delete

To temporarily disable or permanently delete a webhook:
  1. Go to IntegrationsWebhooks
  2. Click on the webhook
  3. Toggle Enabled to disable, or click Delete to remove

Monitoring

Delivery Logs

View webhook delivery history:
  1. Go to IntegrationsWebhooks
  2. Click on a webhook
  3. View the Delivery History tab
Each delivery shows:
  • Event type and ID
  • HTTP response code
  • Response time
  • Retry count (if any)

Failed Deliveries

For failed deliveries, you can:
  • View the error message
  • See retry attempts
  • Manually retry the delivery

Troubleshooting

  1. Verify your endpoint URL is correct and uses HTTPS
  2. Check that your server is publicly accessible
  3. Ensure the webhook is enabled
  4. Check server logs for incoming requests
  1. Verify you’re using the correct webhook secret
  2. Ensure you’re verifying against the raw request body
  3. Check that timestamps haven’t expired (5 minute tolerance)
  1. Ensure your endpoint responds within 30 seconds
  2. Process events asynchronously
  3. Return 200 immediately, handle logic in background
  1. Use the eventId field for idempotency
  2. Store processed event IDs in your database
  3. Skip events that have already been processed