Pular para o conteúdo principal

Entregas e retry

Nenhuma entrega é "dispara e esquece". Cada evento × endpoint vira um registro de entrega com status, tentativas e a captura completa de request e response — o que transforma "o webhook não chegou" de mistério em consulta.

Entregas

O ciclo de vida de uma entrega

StatusSignificado
pendingAgendada, aguardando processamento
successSeu endpoint respondeu 2xx
failedFalhou; retry agendado
exhaustedEsgotou as tentativas (ou falha permanente); foi para a DLQ

Conta como falha: resposta fora de 2xx (redirects não são seguidos, logo 3xx também falha), erro de rede e timeout (10 segundos por tentativa).

O contrato do seu endpoint

Três regras para um consumidor saudável:

  1. Responda 2xx imediatamente, sem processar sincronamente. Grave o payload (fila, tabela, log) e devolva 200 na hora; processe depois. O timeout é de 10 segundos — um handler que consulta banco, chama terceiros e só então responde vai estourar o limite em dia de carga e disparar retries desnecessários.
  2. Evento desconhecido? Responda 200 e ignore. Novos eventos podem passar a ser emitidos sem aviso (endpoint com lista de eventos vazia recebe todos). Responder erro a um evento que você não trata só gera retry e ruído na sua fila.
  3. Deduplique antes de processar — veja "pelo menos uma vez" abaixo.

Retry: 8 tentativas em ~8h30 (backoff exponencial)

Cada entrega tem 8 tentativas com backoff exponencial de base 4 minutos — a espera dobra a cada falha:

TentativaQuando (aprox.)Espera após a falha anterior
imediata
+4min4min
+12min8min
+28min16min
+1h32min
+2h041h04
+4h122h08
+8h284h16

A janela total é de ~8h30 entre o evento e a última tentativa (os horários são aproximados: dependem da fila no momento) — um consumidor fora do ar por minutos ou horas ainda recebe o evento sem intervenção manual. A tela mostra a próxima tentativa estimada (nextRetryAt). Cada tentativa é re-assinada com um t novo na assinatura.

Duas situações pulam o retry e vão direto para exhausted:

  • Bloqueio SSRF: a URL passou a resolver para rede interna — falha permanente, reagendar não ajudaria.
  • Endpoint inativo ou removido no momento da entrega.

DLQ: nada some em silêncio

Quando a 8ª tentativa falha, a entrega vira exhausted e o job vai para a dead-letter queue com o erro registrado. O evento em si permanece persistido e imutável; a entrega esgotada continua listada na tela, pronta para re-envio manual quando você corrigir o endpoint.

A tela de entregas

Na linha do endpoint, abra o histórico (10 por página). Cada entrega mostra evento, status, código HTTP, número de tentativas e data; expandindo a linha:

  • o erro da última tentativa,
  • o request body enviado,
  • o response body devolvido pelo seu servidor,
  • e o momento da entrega bem-sucedida (deliveredAt).

Headers sensíveis (assinatura, credenciais de autenticação, Set-Cookie da resposta) aparecem como [REDACTED]. A listagem de endpoints agrega o total de entregas e destaca em vermelho a soma de falhas + esgotadas.

Re-envio manual

Entregas failed ou exhausted têm o botão de reprocessar (também via POST /api/v1/webhook-endpoints/{id}/deliveries/{deliveryId}/retry). O re-envio reseta a entrega e recomeça o ciclo de tentativas; se o endpoint estiver inativo, o pedido é recusado (409 ENDPOINT_INACTIVE). Cada re-envio fica na auditoria.

Entrega "pelo menos uma vez"

O sistema garante que o evento não se perde, não que chega exatamente uma vez: um 200 que se perdeu na rede gera nova tentativa e, portanto, entrega duplicada no seu lado. Projete o consumidor idempotente: deduplique pelo header X-Webhook-Event-Id (o UUID do evento, estável entre tentativas e re-envios) antes de processar. O header X-Webhook-Attempt traz o número da tentativa, útil para telemetria do seu lado.

Testando sem evento de negócio

POST /api/v1/webhook-endpoints/{id}/test dispara um evento webhook.test pelo pipeline real — a entrega aparece na tela como qualquer outra, com captura e retry. Use para validar assinatura, auth e o contrato do consumidor antes de entrar em produção.