Webhooks

Webhook recomendado; polling 3–5s só como fallback.

Webhooks são a forma recomendada e em tempo real de receber atualizações sobre suas notas fiscais. Em vez de o seu ERP ou servidor ficar consultando a API repetidamente (polling), o NFER envia um HTTP POST instantâneo para a sua URL HTTPS sempre que uma NF-e for autorizada, rejeitada pela SEFAZ ou cancelada.

Por que Usar Webhooks (Webhook-First)?

Tempo Real

Assim que a SEFAZ autoriza o lote, seu sistema recebe a chave de 44 dígitos e o protocolo em milissegundos.

Segurança HMAC

Cada entrega inclui assinatura criptográfica X-NFER-Signature para garantir que o payload veio do NFER.

Retentativas Automáticas

Se seu servidor estiver fora do ar, o NFER tenta reenviar até 8 vezes com backoff exponencial durante 2 horas.

Polling só como fallback

Você ainda pode consultar GET /v1/nfe/:id caso precise de uma verificação manual, mas recomendamos manter um intervalo de 3 a 5 segundos enquanto a nota estiver com status "processando" para não atingir o rate limit.

Catálogo de Eventos Suportados

Você pode se inscrever em todos os eventos ou filtrar apenas os que interessam para a sua aplicação:

EventoGatilho / Quando ocorreAção típica no ERP
nfe.autorizadaSEFAZ autorizou o uso da NF-e (cStat 100/150). Retorna chaveAcesso e protocolo.Libera o pedido para expedição, gera etiqueta de envio e anexa DANFE PDF.
nfe.erroSEFAZ rejeitou a nota (ex: cStat 321, 539, 778). Retorna erroCstat e erroMotivo.Alerta o suporte, corrige o rascunho com PUT /v1/nfe/:id e reenvia.
nfe.canceladaEvento de cancelamento homologado pela SEFAZ (cStat 135/136).Marca o pedido como cancelado e estorna os lançamentos fiscais.
nfe.contingenciaSEFAZ do estado indisponível. Nota autorizada via SVC (Sefaz Virtual de Contingência).Permite a circulação normal da mercadoria com o DANFE impresso em contingência.
nfe.denegadaUso denegado pela SEFAZ (irregularidade fiscal grave do emitente ou destinatário).A nota é gravada mas a mercadoria NÃO pode circular nem o número reutilizado.
nfe.batch.completedProcessamento do lote (POST /v1/nfe/batch) foi finalizado por completo.Atualiza status em massa de pedidos no ERP.

Endpoints de Gerenciamento de Webhooks

MétodoEndpointDescriçãoStatus HTTP
POST/v1/webhooksCadastra um novo endpoint HTTPS com URL, eventos e secret201 Created
GET/v1/webhooksLista as configurações de webhook da empresa ativa200 OK
PUT/v1/webhooks/:idAtualiza a URL de destino, eventos inscritos ou status ativo200 OK
DELETE/v1/webhooks/:idRemove permanentemente a inscrição de webhook204 No Content
POST/v1/webhooks/:id/testDispara um evento de teste simulado para validar seu servidor200 OK
POST/v1/webhooks/:id/rotate-secretGera uma nova chave secret HMAC para o webhook200 OK
GET/v1/webhooks/:id/deliveriesHistórico de entregas, status HTTP e respostas recebidas200 OK

Como Cadastrar um Webhook

Você pode cadastrar o webhook diretamente pelo painel da NFER em Configurações ➡️ Webhooks ou pela API:

POST /v1/webhooks — Cadastrar Webhookbash
curl -X POST https://api.nfer.me/v1/webhooks \
  -H "X-API-Key: $NFER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://seu-sistema.com.br/api/webhooks/nfer",
    "events": [
      "nfe.autorizada",
      "nfe.erro",
      "nfe.cancelada",
      "nfe.contingencia"
    ],
    "secret": "sua_chave_secreta_para_validacao_hmac"
  }'

Resposta com o ID gerado:

Resposta 201 Createdjson
{
  "id": "wh_78a1bc-uuid",
  "url": "https://seu-sistema.com.br/api/webhooks/nfer",
  "events": ["nfe.autorizada", "nfe.erro", "nfe.cancelada", "nfe.contingencia"],
  "active": true,
  "secret": "sua_chave_secreta_para_validacao_hmac",
  "createdAt": "2026-08-20T12:00:00.000Z"
}

Estrutura dos Payloads Recebidos

Todas as entregas de webhook seguem um envelope padrão com identificador único (id), tipo de evento (type), data de envio (createdAt) e o objeto com os dados fiscais (data).

1. Exemplo de Nota Autorizada (nfe.autorizada)

Recebido assim que a SEFAZ processa e aprova o envio.

chaveAcesso: Chave oficial de 44 dígitos.

protocolo: Número do protocolo da SEFAZ.

status: "autorizada".

Payload: nfe.autorizadajson
{
  "id": "deliv_8f1c2a10-4b2e-4e11",
  "type": "nfe.autorizada",
  "createdAt": "2026-08-20T12:04:01.000Z",
  "data": {
    "nfeId": "8696e84a-72fe-41aa-bcd5-bb6165ba1ab7",
    "numero": 1042,
    "serie": 1,
    "status": "autorizada",
    "chaveAcesso": "41260812345678000155650010000010421234567890",
    "protocolo": "141260000012345",
    "erroCstat": null,
    "erroMotivo": null,
    "modelo": "55",
    "environment": "producao"
  }
}

2. Exemplo de Nota com Rejeição (nfe.erro)

Recebido quando a SEFAZ recusa a autorização por inconsistência cadastral ou tributária.

erroCstat: Código oficial do motivo SEFAZ (ex.: 539, 321).

erroMotivo: Descrição clara da rejeição.

Payload: nfe.errojson
{
  "id": "deliv_628576bc-78c2-42f7",
  "type": "nfe.erro",
  "createdAt": "2026-08-20T12:05:12.000Z",
  "data": {
    "nfeId": "8696e84a-72fe-41aa-bcd5-bb6165ba1ab7",
    "numero": 1042,
    "serie": 1,
    "status": "erro",
    "chaveAcesso": "41260812345678000155650010000010421234567890",
    "protocolo": null,
    "erroCstat": "539",
    "erroMotivo": "Rejeição: Duplicidade de NF-e com diferença na Chave de Acesso",
    "modelo": "55",
    "environment": "producao"
  }
}

Segurança: Validação de Assinatura HMAC-SHA256

Quando você cadastra um secret, cada requisição enviada pelo NFER conterá dois cabeçalhos HTTP de segurança para você validar a autenticidade antes de executar qualquer código no seu banco de dados:

Cabeçalho HTTPDescrição
X-NFER-EventTipo do evento que disparou a notificação (ex.: nfe.autorizada).
X-NFER-DeliveryIdentificador único da tentativa de entrega (útil para garantir idempotência).
X-NFER-TimestampTimestamp Unix em segundos do momento do disparo.
X-NFER-SignatureAssinatura sha256=HEX calculada como HMAC-SHA256(secret, "{timestamp}.{rawBody}").

Exemplo de Validação em Node.js (TypeScript / Express / Fastify):

Validação de Assinatura (Node.js)typescript
import crypto from 'crypto';
import type { Request, Response } from 'express';

export function handleNferWebhook(req: Request, res: Response) {
  const secret = process.env.NFER_WEBHOOK_SECRET || '';
  const signature = req.headers['x-nfer-signature'] as string;
  const timestamp = req.headers['x-nfer-timestamp'] as string;

  // Atenção: Use o raw body (bytes originais da requisição antes do JSON.parse)
  const rawBody = (req as any).rawBody || JSON.stringify(req.body);

  if (secret && signature && timestamp) {
    const payloadToSign = `${timestamp}.${rawBody}`;
    const expectedSignature = 'sha256=' + crypto
      .createHmac('sha256', secret)
      .update(payloadToSign)
      .digest('hex');

    // Comparação em tempo constante para proteção contra timing attacks
    const isValid = crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expectedSignature)
    );

    if (!isValid) {
      return res.status(401).json({ error: 'Assinatura inválida' });
    }
  }

  const { type, data } = req.body;

  if (type === 'nfe.autorizada') {
    console.log(`Nota ${data.numero} autorizada! Chave: ${data.chaveAcesso}`);
    // Atualize o status do pedido no seu banco de dados
  }

  // Responda sempre 200 OK rapidamente
  return res.status(200).json({ received: true });
}

Política de Retentativas e Idempotência

Seu servidor deve responder com status HTTP 200 ou 204 em até 15 segundos. Caso seu servidor retorne status 4xx/5xx ou sofra timeout, o NFER iniciará o cronograma de retentativas:

TentativaTempo de Espera após a Falha
1ª TentativaImediata no momento do evento
2ª Tentativa10 segundos após a 1ª falha
3ª Tentativa30 segundos após a 2ª falha
4ª Tentativa1 minuto após a 3ª falha
5ª Tentativa5 minutos após a 4ª falha
6ª Tentativa15 minutos após a 5ª falha
7ª Tentativa30 minutos após a 6ª falha
8ª Tentativa (Final)2 horas após a 7ª falha

Dica de Idempotência

Como eventos podem ser reenviados em caso de lentidão temporária da sua rede, use o cabeçalho X-NFER-Delivery ou o data.nfeId para garantir que a sua lógica de negócio execute apenas uma vez por nota.

Disparo de Teste Manual (POST /v1/webhooks/:id/test)

Você pode disparar um evento simulado a qualquer momento para verificar se o seu endpoint está recebendo e processando os dados corretamente, sem precisar emitir uma nota fiscal real na SEFAZ:

POST /v1/webhooks/:id/testbash
curl -X POST https://api.nfer.me/v1/webhooks/wh_78a1bc-uuid/test \
  -H "X-API-Key: $NFER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "eventType": "nfe.autorizada",
    "nfeId": "8696e84a-72fe-41aa-bcd5-bb6165ba1ab7"
  }'