Working with Webhooks 🗄️ Archived
Set up and handle webhooks for real-time event notifications
Webhooks allow your application to receive real-time notifications when events occur in our system.
What are Webhooks?
Webhooks are HTTP callbacks that deliver event data to your application when specific events occur. Instead of polling for changes, webhooks push notifications to your server immediately.
Setting Up Webhooks
1. Create a Webhook Endpoint
Your endpoint should:
- Accept POST requests
- Respond quickly (within 5 seconds)
- Return a 2xx status code
- Verify the webhook signature
2. Register Your Webhook
curl -X POST https://api.example.com/v1/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks",
"events": [
"resource.created",
"resource.updated",
"resource.deleted"
],
"secret": "your_webhook_secret"
}'Available Events
| Event | Description |
|---|---|
resource.created | New resource created |
resource.updated | Resource updated |
resource.deleted | Resource deleted |
user.created | New user registered |
user.updated | User profile updated |
payment.succeeded | Payment completed |
payment.failed | Payment failed |
Webhook Payload
All webhooks include:
{
"id": "evt_abc123",
"type": "resource.created",
"created_at": "2025-02-18T10:30:00Z",
"data": {
"object": {
"id": "res_xyz789",
"name": "New Resource",
"status": "active"
}
},
"previous_data": null
}For update events, previous_data contains the old values:
{
"id": "evt_def456",
"type": "resource.updated",
"created_at": "2025-02-18T10:35:00Z",
"data": {
"object": {
"id": "res_xyz789",
"name": "Updated Resource",
"status": "active"
}
},
"previous_data": {
"name": "Old Resource Name"
}
}Verifying Webhook Signatures
Always verify that webhooks come from our service:
Python
import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
"""Verify the webhook signature"""
expected_signature = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
# In your webhook handler
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
payload = request.get_data(as_text=True)
signature = request.headers.get('X-Webhook-Signature')
if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
return jsonify({'error': 'Invalid signature'}), 401
event = request.json
# Handle the event
if event['type'] == 'resource.created':
handle_resource_created(event['data']['object'])
elif event['type'] == 'resource.updated':
handle_resource_updated(event['data']['object'])
return jsonify({'status': 'received'}), 200Node.js
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.raw({ type: 'application/json' }));
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhooks', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(payload);
// Handle the event
switch (event.type) {
case 'resource.created':
handleResourceCreated(event.data.object);
break;
case 'resource.updated':
handleResourceUpdated(event.data.object);
break;
}
res.json({ status: 'received' });
});Handling Webhook Events
Asynchronous Processing
Process webhooks asynchronously to respond quickly:
from celery import Celery
app = Celery('tasks', broker='redis://localhost:6379')
@app.task
def process_webhook_event(event):
"""Process webhook event asynchronously"""
if event['type'] == 'resource.created':
# Do time-consuming work here
send_notification(event['data']['object'])
update_analytics(event['data']['object'])
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
# Verify signature...
event = request.json
# Queue for async processing
process_webhook_event.delay(event)
# Respond immediately
return jsonify({'status': 'received'}), 200Idempotency
Handle duplicate webhook deliveries:
from redis import Redis
redis_client = Redis()
def process_webhook_with_idempotency(event):
event_id = event['id']
# Check if we've already processed this event
if redis_client.exists(f'webhook:{event_id}'):
return # Already processed
# Process the event
handle_event(event)
# Mark as processed (expire after 24 hours)
redis_client.setex(f'webhook:{event_id}', 86400, '1')Testing Webhooks
Local Development with ngrok
# Install ngrok
brew install ngrok # macOS
# Start your local server
python app.py
# Expose your local server
ngrok http 5000Use the ngrok URL when registering your webhook.
Manual Testing
Trigger a test webhook:
curl -X POST https://api.example.com/v1/webhooks/test \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhook_id": "wh_abc123",
"event_type": "resource.created"
}'Webhook Retry Logic
If your endpoint fails, we’ll retry:
- Immediately
- After 1 minute
- After 5 minutes
- After 30 minutes
- After 2 hours
After 5 failed attempts, the webhook is disabled.
Managing Webhooks
List All Webhooks
curl https://api.example.com/v1/webhooks \
-H "Authorization: Bearer YOUR_TOKEN"Update a Webhook
curl -X PATCH https://api.example.com/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"events": ["resource.created", "resource.updated"]
}'Delete a Webhook
curl -X DELETE https://api.example.com/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_TOKEN"Best Practices
- Always verify signatures to ensure authenticity
- Respond quickly (within 5 seconds)
- Process asynchronously for time-consuming tasks
- Handle idempotency to avoid duplicate processing
- Log webhook events for debugging
- Monitor webhook health and failures
- Use HTTPS endpoints for security
- Implement rate limiting to prevent abuse