Ejemplos de webhooks
Esta página ofrece ejemplos completos y listos para producción para recibir y procesar webhooks de Ignite en distintos lenguajes.
Ejemplos de servidor completos
Next.js API Routes
// 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 ThumbnailFormat {
url: string;
fileSize: number;
}
interface ThumbnailVariant {
name: string;
width: number;
height: number;
formats: {
jpeg: ThumbnailFormat;
webp: ThumbnailFormat;
};
}
interface VideoWebhookData {
id: string;
status: "COMPLETE" | "PROCESSING" | "NO_FILE" | "FAILED";
title: string;
src?: {
thumbnails?: ThumbnailVariant[];
thumbnailUrl?: string; // deprecated — use src.thumbnails instead
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, {
// thumbnails: video.src?.thumbnails,
// thumbnail: video.src?.thumbnails?.[0]?.formats.jpeg.url,
// sizes: video.src?.mp4,
// });
}
break;
case "video.deleted":
console.log("Video deleted:", video.id);
// await handleVideoDeleted(video);
break;
}
return res.status(204).end();
}Entregas de webhook
Cada intento de entrega de un webhook queda registrado. Si una entrega falla, el sistema reintentará automáticamente.
Estado de la entrega
| Estado | Descripción |
|---|---|
success | El evento se entregó correctamente (respuesta HTTP 2xx) |
failed | La entrega falló (tiempo de espera agotado, HTTP 4xx/5xx, error de red) |
Información de la entrega
Cada registro de entrega incluye:
| Campo | Descripción |
|---|---|
responseCode | Código de estado HTTP devuelto por tu endpoint |
responseBody | Cuerpo de la respuesta de tu endpoint (truncado si es muy largo) |
errorMessage | Mensaje de error si la entrega falló |
deliveredAt | Marca de tiempo del intento de entrega |
attemptNumber | Número de intento (aumenta con los reintentos) |
nextRetryAt | Cuándo se intentará el siguiente reintegro (si aplica) |
Comportamiento de reintentos
- Las entregas fallidas se reintentan automáticamente
- Si
nextRetryAttiene valor, hay un reintegro programado - Si
nextRetryAtno tiene valor, no habrá más reintentos
Buenas prácticas
Seguir estas buenas prácticas mejora la fiabilidad del procesamiento de webhooks y facilita depurar incidencias.
Tiempo de respuesta
Tu endpoint debe responder en 30 segundos como máximo. Para operaciones largas, confirma el webhook al instante y procesa de forma asíncrona:
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
}Seguridad
- Protege los secretos — No expongas el secreto de firma en código del cliente ni en el control de versiones
- Usa HTTPS — En producción, usa siempre endpoints HTTPS
- Valida todo — Verifica siempre las firmas antes de procesar
- Limitación de tasa — Valora aplicar rate limiting en tu endpoint
Fiabilidad
- Idempotencia — Guarda y comprueba
X-Webhook-Delivery-Idpara gestionar entregas duplicadas - Registro — Registra todas las solicitudes de webhook para depurar
- Monitorización — Configura alertas ante fallos en el procesamiento
- Manejo de errores — Devuelve códigos HTTP adecuados para que los reintentos funcionen bien
Códigos de estado HTTP
| Código | Significado |
|---|---|
200-299 | Éxito — evento procesado |
4xx | Error de cliente — se reintentará |
5xx | Error de servidor — se reintentará |
Resolución de problemas
No recibes eventos
- Comprueba que la suscripción al webhook esté activa
- Verifica que los tipos de evento correctos estén seleccionados
- Asegúrate de que los eventos ocurren en el espacio de trabajo correcto
- Revisa los logs del servidor por solicitudes entrantes
Falla la verificación de firma
- Confirma que usas el secreto de firma correcto (no un token de API)
- Asegúrate de que el cuerpo de la solicitud no se modifica antes de verificar
- Comprueba que la serialización JSON coincide (compacta, sin espacios extra)
- Verifica que el formato de la cabecera de firma se parsea bien:
t=timestamp,v1=signature
Errores de tiempo de espera
- Responde en 30 segundos como máximo
- Mueve las operaciones largas a procesamiento en segundo plano
- Devuelve
200de inmediato y procesa de forma asíncrona
Eventos duplicados
- Implementa idempotencia con
X-Webhook-Delivery-Id - Revisa
attemptNumberen los reintentos - Guarda los IDs de entrega procesados en una base de datos