Webhooks18 de abril de 20268 min de leitura

Retry de webhooks da Stripe: um guia para produção

A Stripe tenta de novo webhooks com falha por até 3 dias. Veja como funciona o cronograma, o que quebra quando seu endpoint cai e como projetar para isso.

INTERVALO ENTRE TENTATIVAS · EXPONENCIALagora5 min30 min3 h18 hdesistestatus=failed · payload ainda no log · ainda disponível para replay
Três dias de novas tentativas e depois silêncio. É o seu próprio log que decide se esse silêncio importa.

Quando seu servidor está fora do ar e a Stripe dispara um evento payment_intent.succeeded, o que acontece com o evento? A Stripe tenta de novo para sempre? Por quanto tempo? E como você concilia os eventos que perdeu?

Essa é a pergunta que eu mais ouço de quem está integrando a Stripe, e ela importa porque um webhook que você não consegue processar é receita sobre a qual seu produto não consegue agir. Um cliente acabou de pagar. Sua rotina de liberação do pedido nunca dispara. O painel dele nunca atualiza.

TL;DR

  • A Stripe tenta de novo por 3 dias, com backoff exponencial, e depois para de vez
  • Responda 200 rápido, faça o trabalho de forma assíncrona, registre cada evento antes de processar, seja idempotente
  • Se você não tem tempo de construir tudo isso, um relay de webhooks (um serviço que recebe, guarda e encaminha seus webhooks) dá a você novas tentativas (retries), log e replay (um novo envio de um evento armazenado, feito por você) com a troca de uma única URL

Como a Stripe tenta de novo

A Stripe espera que seu endpoint de webhook responda rápido com um código de status 2xx. Ela não publica um número exato, e a documentação só diz para responder 2xx "antes de qualquer lógica complexa que possa causar um timeout". O número com que a comunidade trabalha é 20 segundos. Uma entrega que deu timeout conta como falha, assim como respostas 5xx e falhas de rede.

Quando a entrega falha, a Stripe tenta enviar o evento de novo, com backoff exponencial, por até 3 dias. As novas tentativas ficam cada vez mais espaçadas, e a última janela fica por volta de 72 horas depois do evento original.

Depois de 3 dias, a Stripe marca o evento como falha e para de tentar de novo. Ela não acorda você. Não envia nenhum e-mail de contingência. O evento simplesmente some da sua fila de entrega, embora você ainda possa vê-lo no Dashboard da Stripe, em Developers → Events.

A curva da Stripe também está entre as mais suaves. Plataformas merchant of record podem tentar de novo com muito mais força: a Paddle faz até 60 tentativas em cerca de dois dias, e um handler que dá conta do cronograma da Stripe ainda pode se afogar no da Paddle.

Se as falhas continuam depois disso, a Stripe acaba desativando o endpoint por completo e manda um e-mail com a data do desligamento. Recuperar-se disso é outro trabalho, e os eventos continuam disponíveis para consulta pela Events API por 30 dias: veja A Stripe desativou seu endpoint de webhook para os passos de preencher a lacuna (backfill).

Os três modos de falha que pegam você

Seu endpoint é lento. A Stripe conta como falha uma resposta que passa do timeout não publicado dela. Se seu handler de webhook grava de forma síncrona num banco lento, sincroniza com o Salesforce e manda um e-mail ao cliente, você está flertando com o timeout em todo evento.

Sua janela de deploy. Seu servidor reinicia durante um deploy, perde cerca de 10 eventos e volta. A Stripe tenta de novo esses eventos, mas só por 3 dias.

Um bug que gera erros 500. Você faz um deploy que lança uma exceção num tipo de evento específico. A Stripe tenta de novo, mas continua esbarrando no mesmo erro. Depois de 3 dias, a Stripe desiste e você fica com uma lacuna silenciosa nos dados.

Nos três casos, a Stripe está fazendo a coisa certa. A falha está do seu lado da conexão.

Quatro coisas que integrações prontas para produção fazem

1. Responda 200 rápido e faça o trabalho de forma assíncrona

Não faça trabalho pesado no handler do webhook. Aceite o evento, grave-o numa fila ou num log durável, responda 200 e processe de forma assíncrona. Isso mantém você longe do timeout e desacopla a entrega da lógica de negócio.

export async function POST(req: Request) {
  const event = await verifyStripeSignature(req);
  await queue.publish(event);   // durable write
  return new Response(null, { status: 200 });
}
waitUntil · O 200 É UMA PROMESSA QUE VOCÊ NÃO PODE CUMPRIRverificarresponder 200waitUntil(work)só na memóriareciclada → perdido, nunca reenviadoARMAZENE PRIMEIRO · O 200 É VERDADEIROverificargravação durávelresponder 200processartenta de novo
Responda 200 para algo que você já gravou de forma durável, e o relógio do provedor deixa de limitar sua lógica de negócio.

2. Registre cada evento antes de processar

Grave o payload bruto e os cabeçalhos antes de tocar na lógica de negócio. Se sua lógica lançar uma exceção, você pode fazer o replay a partir do log. A Stripe para de tentar de novo depois de 3 dias e guarda os eventos por 30, então, passado esse prazo, seu log é a única coisa que permite recuperar.

3. Idempotência do seu lado

A Stripe envia o mesmo campo event.id nas novas tentativas, então seu handler precisa ser idempotente: se você já processou o evento evt_1H..., não processe de novo. Um padrão simples:

INSERT INTO processed_events (stripe_event_id, processed_at)
VALUES ($1, NOW())
ON CONFLICT (stripe_event_id) DO NOTHING;

Se o insert retornar 0 linhas afetadas, você já viu esse evento. Pule.

A idempotência é mais profunda do que parece quando as novas tentativas começam a chegar em paralelo ou fora de ordem. Você recebeu o mesmo webhook duas vezes trata da fronteira da transação, do padrão claim-then-commit para efeitos colaterais que você não consegue desfazer e de por que são as guardas de versão, e não a deduplicação, que resolvem a entrega fora de ordem.

4. Alertas de falha

Se 5 eventos seguidos falham, você quer saber em minutos, não quando um cliente escrever para o suporte. Acompanhe as falhas consecutivas e acione seu próprio plantão.

Onde o AnyHook entra

O AnyHook fica na frente do seu servidor e resolve isso na camada de transporte. Você aponta a Stripe para in.anyhook.net/you/stripe em vez do seu servidor e, a partir daí:

  • Respondemos 200 à Stripe em cerca de um segundo, então a janela de resposta nunca é um problema
  • Registramos cada evento com os cabeçalhos e o corpo completos, com criptografia em repouso
  • Tentamos de novo a entrega ao seu servidor em 5 minutos, 30 minutos, 3 horas, 18 horas e depois uma vez por dia: 3 tentativas no Free, 5 no Pro, 10 no Scale. O cronograma da própria Stripe vai até três dias, então, no Free e no Pro, a rede de segurança longa é o log armazenado e o replay, não o número de novas tentativas
  • Damos a você replay com um clique. Se seu servidor ficou fora do ar por uma hora, a página do app (uma URL de entrada para uma origem) faz o replay dos eventos com falha daquela hora num só clique (até 100 por vez), e replays de eventos com falha não contam na sua cota mensal, embora tenham um limite diário e usem sua franquia de computação
  • Alertamos sobre sequências de falhas. Mandamos um e-mail com 1 e com 5 falhas consecutivas. Com 20, pausamos a entrega para esse destino (10 minutos na primeira vez, mais tempo a cada nova pausa) e mandamos outro e-mail; os eventos que chegam durante uma pausa ficam registrados para replay, em vez de entregues

Sem SDK, só uma troca de URL. A primeira entrega continua passando na sua verificação atual da Stripe. As novas tentativas e os replays levam o timestamp original da Stripe, que o SDK da Stripe recusa depois de cinco minutos; então verifique o cabeçalho AnyHook-Signature para aceitá-los.

Para ver isso antes de se cadastrar em qualquer coisa, um único comando retorna uma URL de entrada ativa, sem conta:

curl -X POST https://anyhook.net/api/v1/quickstart

A resposta traz o campo inbound_url, para apontar um provedor, o campo api_key, para ler o log de eventos pela API, e o campo claim_url. O endpoint é temporário: o AnyHook exclui o endpoint e os eventos dele depois de 7 dias, a menos que você abra a URL de vinculação e entre na sua conta, o que mantém a mesma URL.

Conclusão

Se você está perdendo eventos da Stripe hoje, a solução normalmente não é ter mais novas tentativas. É gravar antes de processar, para sempre ter de onde fazer o replay.

Todos os posts18 de abril de 2026 · 8 min

Pare de perder webhooks.

Troque uma URL. Tenha novas tentativas, um log de eventos e replay com um clique. Ou veja antes: crie uma conta, rode a simulação de queda de 30 segundos e veja um evento sobreviver ao seu servidor fora do ar. Não precisa de servidor.