Garantias de entrega
Duas garantias mudam como se escreve o receptor de um webhook, e elas estão declaradas aqui de propósito. Nenhuma das duas é limitação a esconder: são decisões, e a alternativa a cada uma custaria mais do que vale.
Ao menos uma vez
Seção intitulada “Ao menos uma vez”Todo webhook é entregue pelo menos uma vez. Na esmagadora maioria dos casos, exatamente uma. Mas duplicata é possível, é esperada, e quem recebe precisa tratá-la.
Por que
Seção intitulada “Por que”Entre “entregamos e o receptor pode receber duas vezes” e “podemos deixar de entregar”, a escolha não é difícil: um pagamento processado duas vezes é um bug que se conserta com três linhas de código; um pagamento que nunca chegou é dinheiro perdido e um cliente irritado que não sabe por quê.
Entrega exatamente uma vez não existe sobre HTTP. Quando o receptor recebe a requisição, processa e a resposta se perde na volta, nós não temos como distinguir isso de “a requisição nunca chegou”. As duas situações são o mesmo timeout. Alguém tem que decidir, e a decisão que não perde evento é tentar de novo.
Os casos concretos em que a duplicata acontece:
- A resposta se perdeu. O receptor processou, o
200não chegou até nós, e a tentativa seguinte do cronograma repete a entrega. - O receptor demorou mais que o timeout. Processou até o fim, mas nós já tínhamos desistido daquela tentativa.
- Um processo nosso morreu entre enviar e registrar o resultado. A entrega volta a ser processável e sai de novo.
A receita para deduplicar, com tabela e código, está em deduplicação e ordem.
Sem garantia de ordem
Seção intitulada “Sem garantia de ordem”Os webhooks não chegam necessariamente na ordem em que os eventos aconteceram.
Por que
Seção intitulada “Por que”Não é um detalhe de implementação que um dia melhora: é consequência direta do retry, que é a funcionalidade principal do produto.
Você publica pedido.criado às 10h00 e pedido.pago às 10h01. O endpoint está instável e recusa
o primeiro; o segundo pega uma janela boa e passa:
| Instante | O que acontece |
|---|---|
| 10h00 | pedido.criado → o endpoint responde 500 |
| 10h01 | pedido.pago → entregue |
| 10h00 + 30s… | pedido.criado reagendado, com espera crescente |
| 10h10 | pedido.criado → entregue, nove minutos depois do que veio depois dele |
O retry com espera crescente reordena por construção. Preservar ordem exigiria uma fila serial por destino, em que um evento parado bloqueia todos os seguintes daquele receptor. Seria trocar “chegou fora de ordem” por “não chegou nada enquanto um evento estava travado”. A entrega mais importante do dia ficaria refém do retry de uma que não importa.
Além do retry, o fan-out para vários endpoints e a concorrência entre entregas também não têm ordem definida entre si.
O que nós garantimos
Seção intitulada “O que nós garantimos”Para não parecer que a lista é só de ressalvas, o que fica de pé:
- Nenhum evento aceito se perde. A intenção de entregar é gravada na mesma transação do
evento. Se respondemos
202, a entrega vai ser tentada. - O cronograma de retry é escrito e previsível: uma tentativa imediata e mais oito, a última cerca de 45 horas depois da primeira. Veja o ciclo de vida.
- Toda tentativa fica registrada, com o status e o começo da resposta do receptor.
- Toda entrega é assinada, e a rotação de secret tem uma janela em que as duas assinaturas viajam juntas. Veja verificando a assinatura.
- Esgotado o cronograma, a entrega aparece como esgotada no painel, você recebe e-mail, e o replay fica disponível.