Tema
Webhooks
Receba um POST assinado com o desfecho de cada nota — autorizada, rejeitada ou cancelada — em vez de ficar consultando.
Configurar (self-service)
No painel: Empresa → Webhook de notificações. Você informa a URL (https), recebe o secret de assinatura uma única vez (whsec_...), e a tela tem:
- Enviar teste — dispara um
teste.pingassinado na hora e mostra o status HTTP que o seu servidor respondeu; - Log de entregas — cada tentativa com evento, número da tentativa, status e erro. Se o seu endpoint cair, é aqui que aparece.
Também dá para configurar por nota (campo webhook_url no payload de emissão) — útil para roteamento fino; a configuração da empresa é o padrão.
O contrato de entrega
POST {sua URL}
Content-Type: application/json
X-NFeRapido-Event: documento.processado
X-NFeRapido-Delivery: <uuid — ESTÁVEL entre retries; use para deduplicar>
X-NFeRapido-Signature: sha256=<HMAC-SHA256 do corpo bruto>- 5 tentativas com backoff em caso de falha (timeout de 10s por tentativa).
- Responda 2xx para confirmar. Qualquer outra coisa (ou timeout) conta como falha e agenda retry.
- Entrega é at-least-once: o mesmo desfecho pode chegar mais de uma vez — deduplique por
X-NFeRapido-Delivery.
Verificar a assinatura
⚠️ Use o corpo bruto da requisição (bytes exatamente como recebidos). Re-serializar o JSON muda os bytes e invalida a assinatura.
Node (com o SDK):
js
import { verificarAssinaturaWebhook } from 'nferapido';
app.post('/webhooks/nferapido', express.raw({ type: '*/*' }), (req, res) => {
const ok = verificarAssinaturaWebhook(
req.body,
req.header('X-NFeRapido-Signature'),
process.env.NFERAPIDO_WEBHOOK_SECRET,
);
if (!ok) return res.status(401).end();
const evento = JSON.parse(req.body.toString('utf8'));
res.status(200).end();
});Python:
python
import hmac, hashlib
def verificar(corpo_bruto: bytes, header: str, secret: str) -> bool:
if not header or not header.startswith("sha256="):
return False
esperado = hmac.new(secret.encode(), corpo_bruto, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperado, header.removeprefix("sha256="))O payload
O corpo é o evento canônico documento.processado — o mesmo shape para todos os documentos:
json
{
"evento_id": "uuid",
"tipo_documento": "nfe",
"resultado": "autorizada",
"ambiente": "producao",
"documento": {
"chave": "352608...",
"numero": 42,
"serie": 5,
"protocolo": "135260001234567"
},
"referencia_externa": "pedido-8842"
}resultado: autorizada · rejeitada (com motivo/cStat) · cancelada · denegada · erro. O campo ambiente distingue homologação de produção — filtre no seu lado.
Depuração
- Enviar teste na tela — resposta do seu servidor na hora.
- Log de entregas — se está vermelho aí, o problema é do lado de lá (status/timeout registrados por tentativa).
- Assinatura falhando? Quase sempre é corpo re-serializado por um middleware de JSON — receba o corpo cru na rota do webhook.