Exemplos de webhooks
Esta página oferece exemplos completos e prontos para produção para receber e processar webhooks Ignite em várias linguagens de programação.
Exemplos completos de servidor
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 tentativa de entrega de webhook é registada. Se uma entrega falhar, o sistema volta a tentar automaticamente.
Estado da entrega
| Estado | Descrição |
|---|---|
success | O evento foi entregue com sucesso (resposta HTTP 2xx) |
failed | A entrega falhou (timeout, HTTP 4xx/5xx, erro de rede) |
Informação da entrega
Cada registo de entrega contém:
| Campo | Descrição |
|---|---|
responseCode | Código de estado HTTP devolvido pelo teu endpoint |
responseBody | Corpo da resposta do teu endpoint (truncado para respostas grandes) |
errorMessage | Mensagem de erro se a entrega falhou |
deliveredAt | Timestamp da tentativa de entrega |
attemptNumber | Qual foi esta tentativa (incrementa com novas tentativas) |
nextRetryAt | Quando será feita a próxima nova tentativa (se aplicável) |
Comportamento de novas tentativas
- As entregas falhadas são automaticamente repetidas
- Se
nextRetryAtestiver definido, fica agendada uma nova tentativa - Se
nextRetryAtnão estiver definido, não haverá mais novas tentativas
Boas práticas
Seguir estas boas práticas garante um processamento fiável dos webhooks e ajuda a depurar problemas.
Tempo de resposta
O teu endpoint tem de responder no prazo de 30 segundos. Para operações longas, confirma o webhook de imediato e processa de forma assí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
}Segurança
- Mantém os segredos seguros — Nunca exponhas o signing secret em código client-side ou em controlo de versões
- Usa HTTPS — Usa sempre endpoints HTTPS em produção
- Valida tudo — Verifica sempre as assinaturas antes de processar
- Limite de pedidos — Considera implementar rate limiting no teu endpoint
Fiabilidade
- Idempotência — Guarda e verifica o
X-Webhook-Delivery-Idpara tratar entregas duplicadas - Logging — Regista todos os pedidos de webhook para depuração
- Monitorização — Configura alertas para falhas no processamento de webhooks
- Tratamento de erros — Devolve códigos de estado HTTP adequados para que as novas tentativas funcionem corretamente
Códigos de estado HTTP
| Código | Significado |
|---|---|
200-299 | Sucesso — evento processado |
4xx | Erro do cliente — será repetido |
5xx | Erro do servidor — será repetido |
Resolução de problemas
Não recebes eventos
- Verifica se a subscrição de webhook está Ativa
- Confirma que os tipos de evento corretos estão selecionados
- Garante que os eventos ocorrem no workspace correto
- Consulta os logs do servidor à procura de pedidos recebidos
A verificação da assinatura falha
- Confirma que estás a usar o signing secret correto (não um token de API)
- Garante que o corpo do pedido não é alterado antes da verificação
- Verifica que a serialização JSON coincide (compacta, sem espaços extra)
- Confirma que o formato do cabeçalho de assinatura é analisado corretamente:
t=timestamp,v1=signature
Erros de timeout
- Responde no prazo de 30 segundos
- Move operações longas para processamento em segundo plano
- Devolve
200de imediato e processa de forma assíncrona
Eventos duplicados
- Implementa idempotência com
X-Webhook-Delivery-Id - Verifica
attemptNumberpara novas tentativas - Guarda os IDs de entrega processados numa base de dados