Tema
Recuperar eventos perdidos
Seu servidor ficou fora do ar, o endpoint foi desativado por falhas seguidas, ou um pico estourou o teto horário. O que fazer para não ficar com buracos.
Primeiro: descubra o que aconteceu
Em Integrações › Webhooks › Log, o status de cada entrega diz onde ela parou:
| Status | O que aconteceu | Recuperação |
|---|---|---|
success | Você respondeu 2xx | Nada a fazer |
pending | Aguardando próxima tentativa | Aguarde — o retry ainda vai rodar |
failed | Esgotou as 5 tentativas | Reenvio manual |
dropped | Barrada pelo teto horário, ou o endpoint estava desativado | Reenvio manual |
dropped não é "nunca existiu"
Uma entrega barrada pelo teto aparece no log com o payload guardado, justamente para você distinguir "foi descartada" de "o evento nunca ocorreu". Se ela não estivesse lá, você não teria como saber o que perdeu.
Reenvio manual
Cada linha do log tem Reenviar. Isso reabre a mesma entrega — mesmo corpo, mesmo id de evento — com uma tentativa adicional.
Como o id não muda, seu consumidor deduplica normalmente. Reenviar algo que você já processou é seguro — desde que você tenha implementado a deduplicação ou uma gravação idempotente.
Use quando:
- Você consertou o servidor e quer as entregas
failedde volta - Um pico gerou
droppedpelo teto - O endpoint ficou desativado por um período
Quando o volume é grande
Reenviar dezenas de entregas uma a uma no painel é inviável. Nesse caso, a varredura por período recupera tudo de uma vez, e costuma ser mais rápida:
js
// Recupera o intervalo do incidente. O UPSERT torna repetição inofensiva.
for await (const resposta of varrerRespostas(FORM_ID, {
from: '2026-07-26',
to: '2026-07-28',
})) {
await salvarNoCrm(resposta);
}Isso funciona para respostas. Para eventos de convite (invite.opened, invite.delivered…), o equivalente é listar os convites e ler o effectiveStatus e os timestamps de cada um:
js
for await (const convite of varrerConvites(FORM_ID)) {
await sincronizarStatus(convite.externalId, {
status: convite.effectiveStatus,
enviadoEm: convite.sentAt,
abertoEm: convite.openedAt,
respondidoEm: convite.respondedAt,
falhaEm: convite.failedAt,
motivoFalha: convite.failReason,
});
}O estado atual está sempre disponível pelo REST — o webhook é o atalho, não a única fonte. Isso é o que torna a recuperação sempre possível, mesmo sem log.
O log tem prazo curto
Ele guarda o payload das entregas, e payload de resposta contém dados pessoais. Não conte com ele como arquivo permanente: passado o prazo de retenção, a recuperação é pela varredura, não pelo reenvio.
Evitando o próximo incidente
Responda 2xx antes de processar. A causa mais comum de failed é o timeout de 10s. Confirme o recebimento, enfileire, processe num worker.
js
app.post('/edusati', express.raw({ type: 'application/json' }), async (req, res) => {
const corpoBruto = req.body.toString('utf8');
if (!assinaturaValida(req.get('x-edusati-signature'), corpoBruto)) {
return res.sendStatus(401);
}
await fila.enfileirar(corpoBruto); // rápido: só encosta na fila
res.sendStatus(200);
});Monitore o endpoint. 20 falhas consecutivas o desativam, e a partir daí as entregas em fila são descartadas. Um alerta no seu lado quando o handler começa a errar chega bem antes disso.
Nunca responda 410 por engano. Ele desativa o endpoint na hora, sem retry. Se seu framework devolve 410 para rota desconhecida, confira que o caminho do webhook está mesmo registrado.
Antecipe picos. Vai fazer um envio de 50 mil convites? O teto padrão é de 600 entregas por hora. Fale com o administrador da conta antes — depois do descarte, só o reenvio manual recupera.
Reconcilie por rotina. Uma varredura diária dos últimos dias, com gravação idempotente, transforma qualquer perda em algo que se resolve sozinho até o dia seguinte. É a rede de segurança que dispensa reagir a incidente. Ver Sincronizar com um CRM › Reconcilie periodicamente.
Manutenção programada
Não existe "pausar" um endpoint. Desativar descarta a fila pendente — não a segura para depois.
Se você vai derrubar o servidor para manutenção, as opções são:
- Deixar ligado e falhar. As entregas entram em retry com backoff (até 1h de espera, 5 tentativas). Manutenção curta é absorvida sem perda nenhuma. Esta é a melhor opção na maioria dos casos.
- Deixar um receptor mínimo no ar que só valida a assinatura, grava o corpo cru num arquivo ou fila e responde 200. Processa depois.
- Desativar e reconciliar depois pela varredura por período.
O que não funciona é desativar esperando que a fila aguarde.