Como Projetar Webhooks Confiáveis: Guia Completo

Ashley Innocent

Ashley Innocent

13 março 2026

Como Projetar Webhooks Confiáveis: Guia Completo

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.

💡
Se você está construindo ou testando webhooks, o Apidog te ajuda a testar a entrega de webhooks, validar assinaturas e simular cenários de falha. Você pode testar a lógica de repetição e verificar o tratamento de idempotência.
botão

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:

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:

Não repita em:

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

Segurança

Testando Webhooks com Apidog

Apidog suporta testes de webhook.

Testar Entrega de Webhook

  1. Crie um endpoint de webhook mock no Apidog
  2. Registre o endpoint com o PetstoreAPI
  3. Acione um evento (adote um pet)
  4. Verifique o webhook recebido
  5. 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

  1. Retorne erro 500 do endpoint mock
  2. Verifique as tentativas de repetição com backoff exponencial
  3. Verifique a fila de mensagens mortas após o máximo de repetições

Testar Idempotência

  1. Receba o webhook
  2. Retorne 200
  3. Receba o mesmo webhook novamente (repetição simulada)
  4. Verifique que não há processamento duplicado

Conclusão

Webhooks confiáveis exigem:

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.

botã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.

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs