Webhooks
Examples

Webhook Examples

This page provides complete, production-ready examples for receiving and processing Ignite webhooks in different programming languages.

Complete Server Examples

const express = require('express');
const crypto = require('crypto');
 
const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SIGNING_SECRET;
 
// Parse JSON and preserve raw body for signature verification
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString();
  }
}));
 
function verifyWebhookSignature(rawBody, signatureHeader, secret) {
  const parts = signatureHeader.split(',');
  const timestampPart = parts.find(p => p.startsWith('t='));
  const signaturePart = parts.find(p => p.startsWith('v1='));
  
  if (!timestampPart || !signaturePart) {
    return false;
  }
  
  const timestamp = timestampPart.split('=')[1];
  const receivedSignature = signaturePart.split('=')[1];
  
  // Reconstruct signed payload using raw body
  const signedPayload = `${timestamp}.${rawBody}`;
  const hmac = crypto.createHmac('sha256', secret);
  const calculatedSignature = hmac.update(signedPayload).digest('hex');
  
  try {
    return crypto.timingSafeEqual(
      Buffer.from(receivedSignature),
      Buffer.from(calculatedSignature)
    );
  } catch {
    return false;
  }
}
 
// Store processed delivery IDs (use a database in production)
const processedDeliveries = new Set();
 
app.post('/webhook', async (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const eventType = req.headers['x-webhook-event'];
  const deliveryId = req.headers['x-webhook-delivery-id'];
  
  // 1. Verify signature
  if (!verifyWebhookSignature(req.rawBody, signature, WEBHOOK_SECRET)) {
    console.error('Invalid webhook signature');
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // 2. Check for duplicate deliveries (idempotency)
  if (processedDeliveries.has(deliveryId)) {
    console.log('Duplicate delivery, skipping:', deliveryId);
    return res.status(200).json({ received: true, duplicate: true });
  }
  
  // 3. Get video data from body
  const video = req.body;
  
  // 4. Process based on event type
  try {
    switch(eventType) {
      case 'video.created':
        console.log('New video created:', video.id, video.title);
        await handleVideoCreated(video);
        break;
        
      case 'video.updated':
        console.log('Video updated:', video.id, video.title);
        await handleVideoUpdated(video);
        break;
        
      case 'video.deleted':
        console.log('Video deleted:', video.id);
        await handleVideoDeleted(video);
        break;
        
      default:
        console.log('Unknown event type:', eventType);
    }
    
    // Mark as processed
    processedDeliveries.add(deliveryId);
    
    res.status(200).json({ received: true });
  } catch (error) {
    console.error('Error processing webhook:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});
 
// Event handlers
async function handleVideoCreated(video) {
  // Example: Sync to your CMS
  // await cms.createVideo({
  //   externalId: video.id,
  //   title: video.title,
  //   status: video.status
  // });
}
 
async function handleVideoUpdated(video) {
  // Example: Update your CMS when video processing completes
  // if (video.status === 'COMPLETE') {
  //   await cms.updateVideo(video.id, {
  //     streamUrl: video.src.abr.url,
  //     thumbnailUrl: video.src.thumbnailUrl,
  //     duration: video.duration
  //   });
  // }
}
 
async function handleVideoDeleted(video) {
  // Example: Remove from your CMS
  // await cms.deleteVideo(video.id);
}
 
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Webhook endpoint listening on port ${PORT}`);
});

Webhook Deliveries

Every webhook delivery attempt is logged. If a delivery fails, the system will automatically retry.

Delivery Status

StatusDescription
successEvent was successfully delivered (HTTP 2xx response)
failedDelivery failed (timeout, HTTP 4xx/5xx, network error)

Delivery Information

Each delivery record contains:

FieldDescription
responseCodeHTTP status code returned by your endpoint
responseBodyResponse body from your endpoint (truncated for large responses)
errorMessageError message if delivery failed
deliveredAtTimestamp of the delivery attempt
attemptNumberWhich attempt this was (increments with retries)
nextRetryAtWhen the next retry will be attempted (if applicable)

Retry Behavior

  • Failed deliveries are automatically retried
  • If nextRetryAt is set, a retry is scheduled
  • If nextRetryAt is not set, no more retries will be attempted

Best Practices

Following these best practices ensures reliable webhook processing and helps with debugging issues.

Response Time

Your endpoint must respond within 30 seconds. For long-running operations, acknowledge the webhook immediately and process asynchronously:

app.post('/webhook', async (req, res) => {
  // Immediately acknowledge receipt
  res.status(200).json({ received: true });
  
  // Process asynchronously (don't await)
  processWebhookAsync(req.body).catch(console.error);
});
 
async function processWebhookAsync(video) {
  // Long-running operations here
}

Security

  • Keep secrets secure — Never expose your signing secret in client-side code or version control
  • Use HTTPS — Always use HTTPS endpoints in production
  • Validate everything — Always verify signatures before processing
  • Rate limiting — Consider implementing rate limiting on your endpoint

Reliability

  • Idempotency — Store and check the X-Webhook-Delivery-Id to handle duplicate deliveries
  • Logging — Log all webhook requests for debugging
  • Monitoring — Set up alerts for failed webhook processing
  • Error handling — Return appropriate HTTP status codes so retries work correctly

HTTP Status Codes

CodeMeaning
200-299Success — event processed
4xxClient error — will be retried
5xxServer error — will be retried

Troubleshooting

No Events Received

  1. Check if the webhook subscription is set to Active
  2. Verify the correct event types are selected
  3. Ensure events are happening in the correct workspace
  4. Check your server logs for incoming requests

Signature Verification Fails

  1. Verify you're using the correct signing secret (not an API token)
  2. Ensure the request body is not modified before verification
  3. Check JSON serialization matches (compact, no extra whitespace)
  4. Verify the signature header format is parsed correctly: t=timestamp,v1=signature

Timeout Errors

  1. Respond within 30 seconds
  2. Move long operations to background processing
  3. Return 200 immediately, process asynchronously

Duplicate Events

  1. Implement idempotency using X-Webhook-Delivery-Id
  2. Check attemptNumber for retries
  3. Store processed delivery IDs in a database