Webhook Examples
This page provides complete, production-ready examples for receiving and processing Ignite webhooks in different programming languages.
Complete Server Examples
// pages/api/webhook.ts
import { createHmac, timingSafeEqual } from "crypto";
import { NextApiRequest, NextApiResponse } from "next";
// Disable body parser to access raw body for signature verification
export const config = {
api: {
bodyParser: false,
},
};
interface VideoWebhookData {
id: string;
status: "COMPLETE" | "PROCESSING" | "NO_FILE" | "FAILED";
title: string;
src?: {
thumbnailUrl?: string;
mp4?: Array<{ name: string; url: string; width: number; height: number }>;
};
// ... other video properties
}
async function getRawBody(req: NextApiRequest): Promise<string> {
const chunks: Buffer[] = [];
for await (const chunk of req) {
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
}
return Buffer.concat(chunks).toString("utf-8");
}
function isValidSignature(
rawBody: string,
signatureHeader: string | string[] | undefined,
secret: string
): boolean {
try {
if (typeof signatureHeader !== "string") {
return false;
}
const time = signatureHeader.match(/t=(\d+)/)?.[1];
const token = signatureHeader.match(/v1=(\w+)/)?.[1];
if (!time || !token) {
return false;
}
const signedPayload = `${time}.${rawBody}`;
const hmac = createHmac("sha256", secret);
const calculatedSignature = hmac.update(signedPayload).digest("hex");
return timingSafeEqual(
Buffer.from(token),
Buffer.from(calculatedSignature)
);
} catch {
return false;
}
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "POST") {
return res.status(405).end();
}
const rawBody = await getRawBody(req);
if (
!isValidSignature(
rawBody,
req.headers["x-webhook-signature"],
process.env.WEBHOOK_SIGNING_SECRET!
)
) {
console.error("Invalid webhook signature");
return res.status(403).end();
}
const eventType = req.headers["x-webhook-event"];
const deliveryId = req.headers["x-webhook-delivery-id"];
const video = JSON.parse(rawBody) as VideoWebhookData;
// Process based on event type
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);
if (video.status === "COMPLETE") {
// Video processing finished - update your database
// await db.updateVideo(video.id, {
// thumbnail: video.src?.thumbnailUrl,
// sizes: video.src?.mp4,
// });
}
break;
case "video.deleted":
console.log("Video deleted:", video.id);
// await handleVideoDeleted(video);
break;
}
return res.status(204).end();
}Webhook Deliveries
Every webhook delivery attempt is logged. If a delivery fails, the system will automatically retry.
Delivery Status
| Status | Description |
|---|---|
success | Event was successfully delivered (HTTP 2xx response) |
failed | Delivery failed (timeout, HTTP 4xx/5xx, network error) |
Delivery Information
Each delivery record contains:
| Field | Description |
|---|---|
responseCode | HTTP status code returned by your endpoint |
responseBody | Response body from your endpoint (truncated for large responses) |
errorMessage | Error message if delivery failed |
deliveredAt | Timestamp of the delivery attempt |
attemptNumber | Which attempt this was (increments with retries) |
nextRetryAt | When the next retry will be attempted (if applicable) |
Retry Behavior
- Failed deliveries are automatically retried
- If
nextRetryAtis set, a retry is scheduled - If
nextRetryAtis 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-Idto 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
| Code | Meaning |
|---|---|
200-299 | Success — event processed |
4xx | Client error — will be retried |
5xx | Server error — will be retried |
Troubleshooting
No Events Received
- Check if the webhook subscription is set to Active
- Verify the correct event types are selected
- Ensure events are happening in the correct workspace
- Check your server logs for incoming requests
Signature Verification Fails
- Verify you're using the correct signing secret (not an API token)
- Ensure the request body is not modified before verification
- Check JSON serialization matches (compact, no extra whitespace)
- Verify the signature header format is parsed correctly:
t=timestamp,v1=signature
Timeout Errors
- Respond within 30 seconds
- Move long operations to background processing
- Return
200immediately, process asynchronously
Duplicate Events
- Implement idempotency using
X-Webhook-Delivery-Id - Check
attemptNumberfor retries - Store processed delivery IDs in a database