This guide walks you through setting up webhook subscriptions to receive real-time event notifications.
Creating a Webhook Subscription
Via Dashboard
- Log in to your SendPilot account
- Navigate to Integrations → Webhooks
- Click Add Webhook
- 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
- Click Create
- 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
- Go to Integrations → Webhooks in the dashboard
- Find your webhook subscription
- Click Send Test Event
- Select an event type
- 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:
- Go to Integrations → Webhooks
- Click on the webhook to edit
- Select or deselect event types
- Save changes
Rotate Secrets
To rotate your webhook secret:
- Go to Integrations → Webhooks
- Click on the webhook to edit
- Click Rotate Secret
- Update your server with the new secret
- 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:
- Go to Integrations → Webhooks
- Click on the webhook
- Toggle Enabled to disable, or click Delete to remove
Monitoring
Delivery Logs
View webhook delivery history:
- Go to Integrations → Webhooks
- Click on a webhook
- 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
- Verify your endpoint URL is correct and uses HTTPS
- Check that your server is publicly accessible
- Ensure the webhook is enabled
- Check server logs for incoming requests
Signature verification failing
- Verify you’re using the correct webhook secret
- Ensure you’re verifying against the raw request body
- Check that timestamps haven’t expired (5 minute tolerance)
- Ensure your endpoint responds within 30 seconds
- Process events asynchronously
- Return 200 immediately, handle logic in background
- Use the
eventId field for idempotency
- Store processed event IDs in your database
- Skip events that have already been processed