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
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:
| Evento | Gatilho / Quando ocorre | Ação típica no ERP |
|---|---|---|
nfe.autorizada | SEFAZ 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.erro | SEFAZ 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.cancelada | Evento de cancelamento homologado pela SEFAZ (cStat 135/136). | Marca o pedido como cancelado e estorna os lançamentos fiscais. |
nfe.contingencia | SEFAZ 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.denegada | Uso 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.completed | Processamento do lote (POST /v1/nfe/batch) foi finalizado por completo. | Atualiza status em massa de pedidos no ERP. |
Endpoints de Gerenciamento de Webhooks
| Método | Endpoint | Descrição | Status HTTP |
|---|---|---|---|
| POST | /v1/webhooks | Cadastra um novo endpoint HTTPS com URL, eventos e secret | 201 Created |
| GET | /v1/webhooks | Lista as configurações de webhook da empresa ativa | 200 OK |
| PUT | /v1/webhooks/:id | Atualiza a URL de destino, eventos inscritos ou status ativo | 200 OK |
| DELETE | /v1/webhooks/:id | Remove permanentemente a inscrição de webhook | 204 No Content |
| POST | /v1/webhooks/:id/test | Dispara um evento de teste simulado para validar seu servidor | 200 OK |
| POST | /v1/webhooks/:id/rotate-secret | Gera uma nova chave secret HMAC para o webhook | 200 OK |
| GET | /v1/webhooks/:id/deliveries | Histórico de entregas, status HTTP e respostas recebidas | 200 OK |
Como Cadastrar um Webhook
Você pode cadastrar o webhook diretamente pelo painel da NFER em Configurações ➡️ Webhooks ou pela API:
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:
{
"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".
{
"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.
{
"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 HTTP | Descrição |
|---|---|
X-NFER-Event | Tipo do evento que disparou a notificação (ex.: nfe.autorizada). |
X-NFER-Delivery | Identificador único da tentativa de entrega (útil para garantir idempotência). |
X-NFER-Timestamp | Timestamp Unix em segundos do momento do disparo. |
X-NFER-Signature | Assinatura sha256=HEX calculada como HMAC-SHA256(secret, "{timestamp}.{rawBody}"). |
Exemplo de Validação em Node.js (TypeScript / Express / Fastify):
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:
| Tentativa | Tempo de Espera após a Falha |
|---|---|
| 1ª Tentativa | Imediata no momento do evento |
| 2ª Tentativa | 10 segundos após a 1ª falha |
| 3ª Tentativa | 30 segundos após a 2ª falha |
| 4ª Tentativa | 1 minuto após a 3ª falha |
| 5ª Tentativa | 5 minutos após a 4ª falha |
| 6ª Tentativa | 15 minutos após a 5ª falha |
| 7ª Tentativa | 30 minutos após a 6ª falha |
| 8ª Tentativa (Final) | 2 horas após a 7ª falha |
Dica de Idempotência
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:
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"
}'