Skip to content

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.ping assinado 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

  1. Enviar teste na tela — resposta do seu servidor na hora.
  2. Log de entregas — se está vermelho aí, o problema é do lado de lá (status/timeout registrados por tentativa).
  3. Assinatura falhando? Quase sempre é corpo re-serializado por um middleware de JSON — receba o corpo cru na rota do webhook.

Emissão ilimitada em todos os planos.