Skip to content

Webhook Guide ​

Overview ​

Webhooks are HTTP callback mechanisms that allow applications to automatically send HTTP requests to specified URLs when specific events occur. This guide will help you understand how to configure, use, and manage webhooks.

Quick Start ​

Basic Concepts ​

  • Webhook URL: The endpoint that receives webhook events
  • Event Types: Specific actions that trigger webhooks
  • Payload: Data sent to the webhook URL
  • Signature: Security mechanism to verify webhook authenticity

Creating a Webhook ​

javascript
// Create webhook configuration
const webhook = {
  url: 'https://your-app.com/webhook',
  events: ['user.created', 'user.updated', 'user.deleted'],
  secret: 'your-webhook-secret',
  active: true
};

// Register webhook
const response = await fetch('/api/webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your-api-token'
  },
  body: JSON.stringify(webhook)
});

Supported Event Types ​

User Events ​

  • user.created - User creation
  • user.updated - User information update
  • user.deleted - User deletion
  • user.login - User login
  • user.logout - User logout

Project Events ​

  • project.created - Project creation
  • project.updated - Project update
  • project.deleted - Project deletion
  • project.shared - Project sharing

File Events ​

  • file.uploaded - File upload
  • file.downloaded - File download
  • file.deleted - File deletion
  • file.shared - File sharing

Webhook Payload Format ​

Standard Payload Structure ​

json
{
  "id": "evt_1234567890",
  "type": "user.created",
  "created": 1640995200,
  "data": {
    "object": {
      "id": "user_123",
      "email": "user@example.com",
      "name": "John Doe",
      "created_at": "2023-01-01T00:00:00Z"
    }
  },
  "request": {
    "id": "req_1234567890",
    "idempotency_key": null
  }
}

Event-Specific Payloads ​

User Creation Event ​

json
{
  "type": "user.created",
  "data": {
    "object": {
      "id": "user_123",
      "email": "user@example.com",
      "name": "John Doe",
      "role": "user",
      "created_at": "2023-01-01T00:00:00Z",
      "metadata": {
        "source": "registration_form"
      }
    }
  }
}

Project Update Event ​

json
{
  "type": "project.updated",
  "data": {
    "object": {
      "id": "proj_456",
      "name": "My Project",
      "description": "Updated description",
      "updated_at": "2023-01-01T12:00:00Z",
      "changes": {
        "description": {
          "from": "Old description",
          "to": "Updated description"
        }
      }
    }
  }
}

Receiving and Processing Webhooks ​

Node.js Example ​

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

// Middleware: Verify webhook signature
function verifyWebhookSignature(req, res, next) {
  const signature = req.headers['x-webhook-signature'];
  const payload = JSON.stringify(req.body);
  const secret = process.env.WEBHOOK_SECRET;
  
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  if (signature !== `sha256=${expectedSignature}`) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  next();
}

// Webhook endpoint
app.post('/webhook', express.json(), verifyWebhookSignature, (req, res) => {
  const { type, data } = req.body;
  
  switch (type) {
    case 'user.created':
      handleUserCreated(data.object);
      break;
    case 'user.updated':
      handleUserUpdated(data.object);
      break;
    case 'project.created':
      handleProjectCreated(data.object);
      break;
    default:
      console.log(`Unhandled event type: ${type}`);
  }
  
  res.status(200).json({ received: true });
});

// Event handler functions
function handleUserCreated(user) {
  console.log('New user created:', user.email);
  // Send welcome email
  sendWelcomeEmail(user.email);
}

function handleUserUpdated(user) {
  console.log('User updated:', user.id);
  // Sync user data to other systems
  syncUserData(user);
}

function handleProjectCreated(project) {
  console.log('New project created:', project.name);
  // Initialize project resources
  initializeProjectResources(project);
}

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

Python Flask 示例 ​

python
import hashlib
import hmac
import json
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = 'your-webhook-secret'

def verify_signature(payload, signature):
    """Verify webhook signature"""
    expected_signature = hmac.new(
        WEBHOOK_SECRET.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(f'sha256={expected_signature}', signature)

@app.route('/webhook', methods=['POST'])
def webhook():
    # Get raw payload and signature
    payload = request.get_data()
    signature = request.headers.get('X-Webhook-Signature')
    
    # Verify signature
    if not verify_signature(payload, signature):
        return jsonify({'error': 'Invalid signature'}), 401
    
    # Parse event data
    event = request.get_json()
    event_type = event.get('type')
    data = event.get('data', {})
    
    # Handle different event types
    if event_type == 'user.created':
        handle_user_created(data.get('object'))
    elif event_type == 'user.updated':
        handle_user_updated(data.get('object'))
    elif event_type == 'project.created':
        handle_project_created(data.get('object'))
    else:
        print(f'Unhandled event type: {event_type}')
    
    return jsonify({'received': True})

def handle_user_created(user):
    print(f'New user created: {user.get("email")}')
    # Handle user creation logic

def handle_user_updated(user):
    print(f'User updated: {user.get("id")}')
    # Handle user update logic

def handle_project_created(project):
    print(f'New project created: {project.get("name")}')
    # Handle project creation logic

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

Security ​

Signature Verification ​

All webhook requests include a signature header to verify the authenticity of the request:

javascript
// Generic function to verify signature
function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

HTTPS Requirements ​

  • All webhook URLs must use HTTPS
  • Self-signed certificates are not supported
  • Certificates must be issued by a trusted CA

IP Whitelist ​

javascript
// Configure allowed IP address ranges
const allowedIPs = [
  '192.168.1.0/24',
  '10.0.0.0/8',
  '172.16.0.0/12'
];

function isIPAllowed(ip) {
  // Check if IP is within allowed ranges
  return allowedIPs.some(range => ipInRange(ip, range));
}

Retry Mechanism ​

Automatic Retry ​

  • Initial retry: Immediate retry
  • Subsequent retries: Exponential backoff (1s, 2s, 4s, 8s, 16s)
  • Maximum retry attempts: 5 times
  • Retry conditions: HTTP status code >= 500 or network errors

Retry Configuration ​

javascript
// Configure retry strategy
const retryConfig = {
  maxRetries: 5,
  initialDelay: 1000, // 1 second
  maxDelay: 30000,    // 30 seconds
  backoffFactor: 2,
  retryConditions: [
    (response) => response.status >= 500,
    (error) => error.code === 'ECONNRESET'
  ]
};

Managing Webhooks ​

List Webhooks ​

javascript
// Get all webhooks
const response = await fetch('/api/webhooks', {
  headers: {
    'Authorization': 'Bearer your-api-token'
  }
});

const webhooks = await response.json();

Update Webhook ​

javascript
// Update webhook configuration
const updatedWebhook = {
  url: 'https://new-endpoint.com/webhook',
  events: ['user.created', 'project.updated'],
  active: true
};

const response = await fetch('/api/webhooks/webhook_id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your-api-token'
  },
  body: JSON.stringify(updatedWebhook)
});

Delete Webhook ​

javascript
// Delete webhook
const response = await fetch('/api/webhooks/webhook_id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer your-api-token'
  }
});

Testing Webhooks ​

Send Test Event ​

javascript
// Send test webhook
const testEvent = {
  type: 'user.created',
  data: {
    object: {
      id: 'test_user_123',
      email: 'test@example.com',
      name: 'Test User'
    }
  }
};

const response = await fetch('/api/webhooks/webhook_id/test', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer your-api-token'
  },
  body: JSON.stringify(testEvent)
});

Local Testing Tools ​

Using ngrok for local testing:

bash
# Install ngrok
npm install -g ngrok

# Start local server
node webhook-server.js

# Start ngrok in another terminal
ngrok http 3000

# Use the ngrok provided URL as webhook URL
# Example: https://abc123.ngrok.io/webhook

Monitoring and Logging ​

Event Logs ​

javascript
// Get webhook event logs
const response = await fetch('/api/webhooks/webhook_id/events', {
  headers: {
    'Authorization': 'Bearer your-api-token'
  }
});

const events = await response.json();
console.log('Recent webhook events:', events);

Performance Monitoring ​

javascript
// Monitor webhook performance
const stats = await fetch('/api/webhooks/webhook_id/stats', {
  headers: {
    'Authorization': 'Bearer your-api-token'
  }
}).then(res => res.json());

console.log('Webhook statistics:', {
  successRate: stats.success_rate,
  averageResponseTime: stats.avg_response_time,
  totalEvents: stats.total_events,
  failedEvents: stats.failed_events
});

Troubleshooting ​

Common Issues ​

Webhook Not Received ​

  1. Check URL Accessibility

    bash
    curl -X POST https://your-app.com/webhook \
      -H "Content-Type: application/json" \
      -d '{"test": true}'
  2. Verify Firewall Settings

    • Ensure ports are open
    • Check IP whitelist configuration
  3. Check SSL Certificate

    bash
    openssl s_client -connect your-app.com:443 -servername your-app.com

Signature Verification Failed ​

javascript
// Debug signature verification
function debugSignatureVerification(payload, receivedSignature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');
  
  console.log('Received signature:', receivedSignature);
  console.log('Expected signature:', `sha256=${expectedSignature}`);
  console.log('Payload:', payload);
  console.log('Secret length:', secret.length);
}

Too Many Retries ​

  1. Check Response Status Code

    • Ensure 2xx status codes are returned
    • Avoid returning 5xx errors
  2. Optimize Response Time

    javascript
    // Asynchronous processing, quick response
    app.post('/webhook', (req, res) => {
      // Immediate response
      res.status(200).json({ received: true });
      
      // Asynchronous event processing
      setImmediate(() => {
        processWebhookEvent(req.body);
      });
    });

Debugging Tools ​

Webhook Debugger ​

javascript
// Simple webhook debugger
const express = require('express');
const app = express();

app.use(express.json());

app.post('/debug-webhook', (req, res) => {
  console.log('=== Webhook Debug Info ===');
  console.log('Headers:', req.headers);
  console.log('Body:', JSON.stringify(req.body, null, 2));
  console.log('Timestamp:', new Date().toISOString());
  console.log('========================');
  
  res.status(200).json({ 
    received: true, 
    timestamp: new Date().toISOString() 
  });
});

app.listen(3001, () => {
  console.log('Webhook debugger running on port 3001');
});

Best Practices ​

Idempotency ​

javascript
// Implement idempotent processing
const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const eventId = req.body.id;
  
  // Check if event has been processed
  if (processedEvents.has(eventId)) {
    return res.status(200).json({ received: true, duplicate: true });
  }
  
  // Process event
  processEvent(req.body);
  
  // Record processed event
  processedEvents.add(eventId);
  
  res.status(200).json({ received: true });
});

Asynchronous Processing ​

javascript
// Use queue for asynchronous processing
const Queue = require('bull');
const webhookQueue = new Queue('webhook processing');

app.post('/webhook', (req, res) => {
  // Immediate response
  res.status(200).json({ received: true });
  
  // Add to queue for asynchronous processing
  webhookQueue.add('process-event', req.body, {
    attempts: 3,
    backoff: 'exponential',
    delay: 1000
  });
});

// Process events in queue
webhookQueue.process('process-event', async (job) => {
  const event = job.data;
  await processWebhookEvent(event);
});

Error Handling ​

javascript
// Comprehensive error handling
app.post('/webhook', async (req, res) => {
  try {
    // Verify signature
    if (!verifySignature(req)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    // Validate payload
    const event = validateEvent(req.body);
    if (!event.valid) {
      return res.status(400).json({ error: event.error });
    }
    
    // Process event
    await processEvent(req.body);
    
    res.status(200).json({ received: true });
  } catch (error) {
    console.error('Webhook processing error:', error);
    
    // Return 5xx error to trigger retry
    res.status(500).json({ 
      error: 'Internal server error',
      id: generateErrorId()
    });
  }
});

Your Ultimate AI-Powered IDE Learning Guide