Retries e boas práticas de webhooks
Atualizado em
Nesta página
A entrega de webhooks é "pelo menos uma vez": um mesmo evento pode chegar mais de uma vez, e uma entrega que falhou é repetida com espera crescente. Monte o receptor contando com isso.
O que conta como sucesso#
- A Emitra espera a resposta por até 10 segundos.
- Qualquer resposta
2xxencerra a entrega com sucesso. - Qualquer outra resposta (
3xx,4xx,5xx), timeout ou erro de conexão conta como falha e agenda uma nova tentativa. - Redirects não são seguidos. Um
301ou302é tratado como falha.
Agenda de tentativas#
São até 7 tentativas por entrega: a primeira e mais seis.
| Tentativa | Quando |
|---|---|
| 1 | Logo após o evento |
| 2 | 1 minuto depois da anterior |
| 3 | 5 minutos depois |
| 4 | 30 minutos depois |
| 5 | 2 horas depois |
| 6 | 6 horas depois |
| 7 | 24 horas depois |
Depois da sétima falha, a entrega fica com status failed e não é mais tentada automaticamente.
Desativação automática#
Se um endpoint acumular 20 falhas seguidas, ele é desativado e os administradores da empresa são avisados. Eventos novos deixam de ser enviados para ele até você reativá-lo no painel ou com PATCH /integracao/webhooks/{id}.
Log e reenvio manual#
No painel, em Notas › Automação › Integrações › Webhooks, cada endpoint mostra o log de entregas: evento, status HTTP, número de tentativas, duração e o primeiro 1 KB da resposta. Dali você pode reenviar uma entrega específica.
Pela API, o mesmo histórico fica em GET /integracao/webhooks/{id}/entregas. Veja a referência.
O log é guardado por 90 dias.
Boas práticas#
Responda rápido, processe depois#
Valide a assinatura, grave o evento numa fila ou tabela e responda 200. Faça o trabalho pesado (baixar o XML, atualizar o ERP, mandar e-mail) num worker. Um receptor que demora mais de 10 segundos gera tentativas repetidas e pode acabar desativado.
Seja idempotente#
Use um destes identificadores para não processar o mesmo evento duas vezes:
X-Emitra-Delivery: identifica a entrega. Se o mesmo evento for enviado para dois endpoints, cada um recebe um delivery diferente.iddo corpo: identifica o evento e é estável entre tentativas e endpoints.
Para a maioria dos casos, grave o id do evento numa tabela com restrição de unicidade e ignore repetidos.
CREATE TABLE emitra_eventos_recebidos (
event_id uuid PRIMARY KEY,
recebido_em timestamptz NOT NULL DEFAULT now()
);
-- No receptor: se o INSERT não inserir nada, o evento já foi processado
INSERT INTO emitra_eventos_recebidos (event_id) VALUES ($1)
ON CONFLICT (event_id) DO NOTHING;Não dependa da ordem#
As entregas podem chegar fora de ordem, principalmente depois de tentativas. Use created_at do evento e, na dúvida, consulte o estado atual pela API (GET /integracao/nfse/{id}) antes de agir.
Separe teste e produção#
Eventos de chaves de teste chegam com is_test: true e o header X-Emitra-Test: true. Recomendamos endpoints diferentes para sandbox e produção, ou pelo menos descartar is_test no ambiente de produção.
Verifique sempre a assinatura#
Veja Verificar a assinatura.
Requisitos da URL#
Para proteger a sua rede e a nossa, a URL do webhook precisa:
- usar https (http é recusado no cadastro);
- responder na porta 443;
- resolver para um IP público. IPs privados, loopback, link-local e endereços de metadata de nuvem (como
169.254.169.254) são recusados; - responder diretamente, sem redirect.
Teste o endpoint#
Envie um evento de teste para conferir conectividade e assinatura:
curl -X POST "https://emitra.com.br/api/v1/integracao/webhooks/b7d4e2f1-9c3a-4e8b-8f65-1a2b3c4d5e6f/testar" \
-H "Authorization: Bearer $EMITRA_API_KEY"import os
import requests
r = requests.post(
"https://emitra.com.br/api/v1/integracao/webhooks/b7d4e2f1-9c3a-4e8b-8f65-1a2b3c4d5e6f/testar",
headers={"Authorization": f"Bearer {os.environ['EMITRA_API_KEY']}"},
timeout=30,
)
print(r.json())const r = await fetch(
"https://emitra.com.br/api/v1/integracao/webhooks/b7d4e2f1-9c3a-4e8b-8f65-1a2b3c4d5e6f/testar",
{
method: "POST",
headers: { Authorization: `Bearer ${process.env.EMITRA_API_KEY}` },
},
);
console.log(await r.json());<?php
$ch = curl_init('https://emitra.com.br/api/v1/integracao/webhooks/b7d4e2f1-9c3a-4e8b-8f65-1a2b3c4d5e6f/testar');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('EMITRA_API_KEY')],
]);
echo curl_exec($ch);A chave precisa do escopo webhooks:manage.