TL;DR
Projete webhooks confiáveis com repetição de backoff exponencial (5-10 tentativas), chaves de idempotência, verificação de assinatura HMAC e tempos limite de 5 segundos. Retorne 2xx imediatamente, processe assincronamente. O Modern PetstoreAPI implementa webhooks para atualizações de pedidos, adoções de pets e notificações de pagamento com repetição completa e segurança.
Introdução
Você envia um webhook para notificar um cliente de que seu pet foi adotado. O servidor do cliente está inativo. Seu webhook falha. Você tenta novamente? Quantas vezes? E se o cliente receber o webhook duas vezes e cobrar o cliente em duplicidade?
Webhooks são callbacks HTTP que enviam eventos para URLs de clientes. São simples na teoria, mas complexos na prática. Redes falham, servidores travam e clientes têm bugs. Webhooks em produção precisam de lógica de repetição, idempotência, segurança e monitoramento.
Modern PetstoreAPI implementa webhooks prontos para produção para atualizações de pedidos, adoções de pets e notificações de pagamento. Cada webhook inclui lógica de repetição, verificação de assinatura e idempotência.
Neste guia, você aprenderá a projetar webhooks confiáveis usando os padrões do Modern PetstoreAPI.
Fundamentos de Webhook
Webhooks são requisições HTTP POST enviadas para URLs fornecidas pelo cliente quando eventos ocorrem.
Como os Webhooks Funcionam
1. Cliente registra URL do webhook:
POST /webhooks
{
"url": "https://client.com/webhooks/petstore",
"events": ["pet.adopted", "order.completed"]
}
2. Evento ocorre (pet adotado)
3. Servidor envia webhook:
POST https://client.com/webhooks/petstore
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
{
"event": "pet.adopted",
"data": {
"petId": "019b4132",
"userId": "user-456",
"timestamp": "2026-03-13T10:30:00Z"
}
}
4. Cliente responde:
200 OK
O Problema de Confiabilidade
Webhooks podem falhar por muitos motivos:
- Servidor do cliente está inativo
- Tempo limite de rede
- Cliente retorna erro 500
- Cliente está lento (leva 30 segundos)
- Cliente recebe webhook, mas falha antes de processar
Sem lógica de repetição, eventos são perdidos. Sem idempotência, webhooks duplicados causam ações duplicadas.
Lógica de Repetição com Backoff Exponencial
Repita webhooks com falha com atrasos crescentes.
Estratégia de Backoff Exponencial
Tentativa 1: Imediata
Tentativa 2: 1 segundo depois
Tentativa 3: 2 segundos depois
Tentativa 4: 4 segundos depois
Tentativa 5: 8 segundos depois
Tentativa 6: 16 segundos depois
Por que exponencial? Se o cliente estiver inativo, sobrecarregá-lo com repetições não ajudará. O backoff exponencial dá tempo para recuperação.
Implementação
async function sendWebhook(url, payload, attempt = 1, maxAttempts = 6) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': generateSignature(payload)
},
body: JSON.stringify(payload),
timeout: 5000 // 5 second timeout
});
if (response.ok) {
return { success: true, attempt };
}
// Retry on 5xx errors
if (response.status >= 500 && attempt < maxAttempts) {
const delay = Math.pow(2, attempt - 1) * 1000;
await sleep(delay);
return sendWebhook(url, payload, attempt + 1, maxAttempts);
}
// Don't retry 4xx errors (client error)
return { success: false, status: response.status };
} catch (error) {
// Network error or timeout - retry
if (attempt < maxAttempts) {
const delay = Math.pow(2, attempt - 1) * 1000;
await sleep(delay);
return sendWebhook(url, payload, attempt + 1, maxAttempts);
}
return { success: false, error: error.message };
}
}
Quando Repetir
Repita em:
- Erros de servidor 5xx (500, 502, 503, 504)
- Tempos limite de rede
- Conexão recusada
- Falhas de DNS
Não repita em:
- Erros de cliente 4xx (400, 401, 404) - o cliente não os corrigirá
- Sucesso 2xx - já foi bem-sucedido
Fila de Mensagens Mortas (Dead Letter Queue)
Após o número máximo de repetições, mova os webhooks com falha para uma fila de mensagens mortas para revisão manual:
if (!result.success) {
await deadLetterQueue.add({
url,
payload,
attempts: maxAttempts,
lastError: result.error,
timestamp: new Date()
});
}
Idempotência para Prevenção de Duplicatas
Clientes podem receber o mesmo webhook várias vezes. A idempotência previne processamento duplicado.
Chaves de Idempotência
Inclua um ID único com cada webhook:
{
"id": "webhook_019b4132",
"event": "pet.adopted",
"data": {...}
}
Cliente armazena IDs processados:
app.post('/webhooks/petstore', async (req, res) => {
const webhookId = req.body.id;
// Check if already processed
const processed = await db.webhooks.findOne({ id: webhookId });
if (processed) {
return res.status(200).json({ message: 'Already processed' });
}
// Process webhook
await processPetAdoption(req.body.data);
// Mark as processed
await db.webhooks.insert({ id: webhookId, processedAt: new Date() });
res.status(200).json({ message: 'Processed' });
});
Operações Idempotentes
Projete operações para serem idempotentes:
Ruim (não idempotente):
// Charging twice causes double charge
await chargeCustomer(userId, amount);
Bom (idempotente):
// Charging with idempotency key prevents double charge
await chargeCustomer(userId, amount, { idempotencyKey: webhookId });
Verificação de Assinatura para Segurança
Verifique se os webhooks vêm da sua API, e não de um invasor.
Assinatura HMAC
Gere assinatura usando segredo compartilhado:
// Server generates signature
const crypto = require('crypto');
function generateSignature(payload, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(JSON.stringify(payload));
return hmac.digest('hex');
}
// Include in header
headers['X-Webhook-Signature'] = `sha256=${generateSignature(payload, webhookSecret)}`;
Cliente verifica assinatura:
function verifySignature(payload, signature, secret) {
const expected = generateSignature(payload, secret);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
app.post('/webhooks/petstore', (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook
...
});
Validação de Carimbo de Tempo
Inclua carimbo de tempo para evitar ataques de repetição:
{
"id": "webhook_019b4132",
"timestamp": "2026-03-13T10:30:00Z",
"event": "pet.adopted",
"data": {...}
}
Rejeite webhooks antigos:
const webhookAge = Date.now() - new Date(req.body.timestamp);
if (webhookAge > 5 * 60 * 1000) { // 5 minutes
return res.status(400).json({ error: 'Webhook too old' });
}
Tratamento de Tempo Limite
Defina tempos limite agressivos para evitar que clientes lentos bloqueiem seu sistema.
Tempo Limite de 5 Segundos
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(payload),
timeout: 5000 // 5 segundos
});
Por que 5 segundos? Webhooks devem retornar imediatamente. Se um cliente demora mais, ele está fazendo processamento síncrono (padrão errado).
Padrão de Processamento Assíncrono
Ruim (síncrono):
app.post('/webhooks/petstore', async (req, res) => {
// Isto leva 30 segundos - o webhook terá tempo limite
await processOrder(req.body.data);
await sendEmail(req.body.data);
await updateInventory(req.body.data);
res.status(200).json({ message: 'Processed' });
});
Bom (assíncrono):
app.post('/webhooks/petstore', async (req, res) => {
// Retorna imediatamente
res.status(200).json({ message: 'Received' });
// Processa assincronamente
queue.add('process-webhook', req.body);
});
Como o Modern PetstoreAPI Implementa Webhooks
Modern PetstoreAPI implementa webhooks prontos para produção.
Eventos de Webhook
pet.adopted - Pet foi adotado
pet.status_changed - Status do pet mudou
order.created - Pedido criado
order.completed - Pedido concluído
payment.succeeded - Pagamento bem-sucedido
payment.failed - Pagamento falhou
Payload do Webhook
{
"id": "webhook_019b4132-70aa-764f-b315-e2803d882a24",
"event": "pet.adopted",
"timestamp": "2026-03-13T10:30:00Z",
"data": {
"petId": "019b4132-70aa-764f-b315-e2803d882a24",
"userId": "user-456",
"orderId": "order-789",
"adoptionDate": "2026-03-13"
},
"apiVersion": "v1"
}
Configuração de Repetição
- Máximo de tentativas: 10
- Backoff: Exponencial (1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 512s)
- Janela total de repetição: ~17 minutos
- Fila de mensagens mortas após o máximo de repetições
Segurança
- Assinatura HMAC-SHA256 no cabeçalho
X-Webhook-Signature - Validação de carimbo de tempo (rejeita > 5 minutos de idade)
- HTTPS exigido para URLs de webhook
Testando Webhooks com Apidog
Apidog suporta testes de webhook.
Testar Entrega de Webhook
- Crie um endpoint de webhook mock no Apidog
- Registre o endpoint com o PetstoreAPI
- Acione um evento (adote um pet)
- Verifique o webhook recebido
- Verifique o formato do payload
Testar Verificação de Assinatura
// Apidog test script
const signature = pm.request.headers.get('X-Webhook-Signature');
const payload = pm.request.body.raw;
const secret = pm.environment.get('WEBHOOK_SECRET');
const expected = generateSignature(payload, secret);
pm.test('Signature valid', () => {
pm.expect(signature).to.equal(`sha256=${expected}`);
});
Testar Lógica de Repetição
- Retorne erro 500 do endpoint mock
- Verifique as tentativas de repetição com backoff exponencial
- Verifique a fila de mensagens mortas após o máximo de repetições
Testar Idempotência
- Receba o webhook
- Retorne 200
- Receba o mesmo webhook novamente (repetição simulada)
- Verifique que não há processamento duplicado
Conclusão
Webhooks confiáveis exigem:
- Repetição com backoff exponencial (5-10 tentativas)
- Chaves de idempotência para evitar duplicatas
- Verificação de assinatura HMAC para segurança
- Tempos limite de 5 segundos
- Processamento assíncrono no lado do cliente
- Fila de mensagens mortas para webhooks com falha
Modern PetstoreAPI implementa todos esses padrões. Verifique a documentação de webhook para exemplos completos.
Teste seus webhooks com o Apidog para verificar a lógica de repetição, assinaturas e idempotência antes de ir para produção.
FAQ
Quantas tentativas de repetição os webhooks devem ter?
5-10 tentativas com backoff exponencial. Isso cobre interrupções temporárias (5-17 minutos) sem sobrecarregar o cliente.
Os webhooks devem tentar novamente em erros 4xx?
Não. Erros 4xx indicam problemas do cliente (URL incorreta, falha de autenticação). Repetir não resolverá isso. Tente novamente apenas em erros 5xx e falhas de rede.
Quanto tempo os tempos limite de webhook devem ser?
No máximo 5 segundos. Os clientes devem retornar 200 imediatamente e processar assincronamente. Tempos limite mais longos indicam que o cliente está fazendo processamento síncrono.
E se um cliente nunca responde aos webhooks?
Após o máximo de repetições, mova para a fila de mensagens mortas. Alerte o cliente por e-mail. Considere desabilitar webhooks para esse cliente após falhas repetidas.
As URLs de webhook devem ser HTTPS?
Sim, sempre exija HTTPS. Webhooks HTTP podem ser interceptados e modificados. O Modern PetstoreAPI rejeita URLs de webhook HTTP.
Como evitar ataques de repetição?
Inclua um carimbo de tempo no payload e rejeite webhooks com mais de 5 minutos. Combine com a verificação de assinatura.
Os clientes podem solicitar a reentrega de webhook?
Sim. O Modern PetstoreAPI oferece um endpoint para reentregar webhooks específicos: POST /webhooks/{id}/redeliver
Como testar webhooks localmente?
Use ferramentas como ngrok para expor o localhost à internet, ou use o servidor mock do Apidog para simular endpoints de webhook durante o desenvolvimento.
