Pular para o conteúdo principal

Webhook events + validação HMAC

A NFE.io envia HTTP POST ao endpoint configurado em webhookUrl da empresa sempre que um documento NF-e ou CT-e novo (ou evento associado) é capturado do Ambiente Nacional da SEFAZ. Este documento cataloga os eventos, o shape do payload, a política de entrega e os mecanismos de segurança.

O modelo evolui com a legislação

Os eventos de NF-e e CT-e recebidos acompanham o calendário oficial da Reforma Tributária e as atualizações do Ambiente Nacional da SEFAZ. Esta página reflete o que está em vigor na data em last_update — novos eventos são habilitados de forma alinhada aos marcos regulatórios.

Sumário​

Política de entrega​

  • Entrega: at-least-once — seu handler deve ser idempotente (deduplique por X-Hook-Id).
  • Method: POST com Content-Type: application/json; charset=utf-8.
  • Retry e timeout: seguem a política única de entrega da plataforma — veja Catálogo de eventos — Política de entrega. Valores atuais: até 16 tentativas ao longo de cerca de 45 horas; timeout de 10 s nas três primeiras tentativas e de 100 s a partir da quarta. São parâmetros de operação e podem ser ajustados — responda 2xx rápido e dimensione com margem.
  • Códigos: 2xx (e 410) confirma a entrega; 400, 401, 403, 404, 405 e 422 são recusa definitiva, sem reentrega; qualquer outro código, erro de rede ou timeout gera reentrega.
  • Assinatura: header x-hub-signature com HMAC-SHA1 do body bruto, formato sha1=<HEX> — ver Validação HMAC.

Esgotadas as tentativas, o evento deixa de ser reenviado automaticamente; o documento continua consultável via API. Para reenviar uma NF-e: POST /v2/companies/{companyId}/inbound/productinvoices/{access_key}/processwebhook.

Eventos​

Quando um documento é recebido, fazemos um POST para sua URL. O tipo de evento chega em dois lugares: o header X-Hook-Event identifica a área (NF-e ou CT-e) e o campo action, na raiz do corpo, identifica a ação específica.

X-Hook-EventactionQuando ocorre
product_invoice_inboundissued_successfully | outbound_successfullyNF-e recebida (ou emitida pela própria conta, se você optou por recebê-las)
product_invoice_inboundinput_event_raised_successfullyEvento de manifestação do destinatário (Ciência, Confirmação, Desconhecimento, Operação não Realizada)
product_invoice_inboundevent_raised_successfullyOutro evento de NF-e (cancelamento, CC-e, EPEC, eventos NT 2025.002)
product_invoice_inbound_summarymesmos valores acimaVariante resumida (sem XML completo) do mesmo evento
transportation_invoice_inboundissued_successfully | outbound_successfullyCT-e recebido (ou emitido pela própria conta)
transportation_invoice_inboundevent_raised_successfullyEvento de CT-e

Formato do Payload​

Os campos vêm achatados na raiz do corpo — sem envelope {"payload": {...}} nem {"body": {...}}. Exemplo de NF-e recebida (X-Hook-Event: product_invoice_inbound, action: issued_successfully):

{
"action": "issued_successfully",
"accountId": "5f9a1b2c3d4e5f6a7b8c9d0e",
"accessKey": "35240112345678000195550010000012341234567890",
"createdOn": "2024-03-15T14:22:10Z",
"parentAccessKey": "",
"company": {
"id": "comp_123",
"federalTaxNumber": "98765432000100"
},
"issuer": {
"federalTaxNumber": "12345678000195",
"name": "Fornecedor LTDA"
},
"buyer": {
"federalTaxNumber": "98765432000100",
"name": "Minha Empresa S.A."
},
"links": {
"xml": "https://api.nfe.io/v2/companies/comp_123/inbound/nfe/35240112345678000195550010000012341234567890/xml",
"pdf": "https://api.nfe.io/v2/companies/comp_123/inbound/nfe/35240112345678000195550010000012341234567890/pdf"
},
"blobUrl": "comp_123/2024/03/35240112345678000195550010000012341234567890.xml",
"type": "productInvoice",
"nsu": "21825",
"nsuParent": "",
"nfeNumber": "1234",
"nfeSerialNumber": "1",
"issuedOn": "2024-03-15T10:00:00Z",
"description": "Autorizado o uso da NF-e",
"totalInvoiceAmount": "1500.00",
"operationType": "Incoming",
"environmentType": 1,
"direction": "Received"
}

Note que totalInvoiceAmount é string, e o identificador da empresa vem em company (objeto), não em um campo solto companyId.

Outros pontos do payload de NF-e:

  • nsu e nsuParent são strings, não números.
  • direction: Received (nota recebida de terceiro) ou Issued (nota emitida pela própria empresa que retornou na distribuição).
  • blobUrl é uma referência interna de armazenamento. Não dependa dele; para baixar arquivos use links.xml/links.pdf ou as rotas /xml e /pdf.
  • issuedOn: na NF-e completa (productInvoice) é a data de emissão (dhEmi). Nos resumos (product_invoice_inbound_summary, type productInvoiceSummary ou productInvoiceEventSummary) é a data de recebimento na SEFAZ (dhRecbto) ou do evento (dhEvento) — por desenho, porque o resumo não traz dhEmi. Com webhookVersion 3, o valor sai no offset do XML (ex.: -03:00); um defeito que adiantava o issuedOn dos resumos em 3h foi corrigido. Com webhookVersion 2 ou anterior, o formato segue +00:00, sem mudança.
A manifestação não gera webhook de conclusão

O resultado de uma manifestação enviada por você (POST .../manifestation-events ou o legado POST /manifest) não é notificado por webhook: consulte o evento por polling em GET .../productinvoices/manifestation-events/{id}. Veja Manifestar NF-e.

Para CT-e, os campos issuer (transportadora) e taker (tomador do serviço) substituem issuer/buyer. Para eventos (action = event_raised_successfully ou input_event_raised_successfully), o corpo carrega também o eventCode e o tipo de evento associado.

Se você cadastrou propriedades personalizadas na assinatura do webhook, elas chegam num objeto properties na raiz do corpo. Sem propriedades cadastradas, o campo não aparece.

Fluxo end-to-end​

Validação HMAC​

A NFE.io assina cada POST com HMAC-SHA1 sobre o body bruto. Validar a assinatura é obrigatório — sem isso, qualquer um que descubra a URL do seu endpoint pode forjar requisições.

Mesmo mecanismo em toda a plataforma

Todos os webhooks entregues pelo serviço central de webhooks da NFE.io são assinados pelo mesmo mecanismo, independente do produto de origem — emissão de NF-e, NFC-e e NFS-e, e captura de documentos recebidos. As especificações desta seção valem para qualquer webhook não legado.

ItemValor
Headerx-hub-signature (lowercase)
AlgoritmoHMAC-SHA1
Encodinghex MAIÚSCULO, sem separadores
Formatosha1=<HEX> (prefixo obrigatório, 45 chars total)
Conteúdo assinadoBody bruto UTF-8, exatamente como recebido
SecretString ASCII de 32 a 64 caracteres, configurada na subscrição

Vetor de teste​

Secret: SuperSecretWebhookKey12345678901
Body: {"event":"test","id":"abc123","amount":42.00}
→ Header: sha1=502BC91DE70F6802FC16CD2E599A9AF752064FE5

Se sua implementação não gera exatamente esse hash com esses inputs, ela tem um bug — corrija antes de testar contra webhook real.

Exemplo (Python/Flask)​

import hmac, hashlib
from flask import request, abort

WEBHOOK_SECRET = b"<seu-segredo-32-a-64-chars>"

@app.post("/webhook/nfeio")
def receive():
sig = request.headers.get("x-hub-signature", "")
if not sig.startswith("sha1="):
abort(401)
expected = "sha1=" + hmac.new(
WEBHOOK_SECRET, request.get_data(cache=True), hashlib.sha1
).hexdigest().upper()
if not hmac.compare_digest(expected, sig):
abort(401)
# processar request.json — veja X-Hook-Event / request.json["action"]
return "", 200

Use comparacão timing-safe (hmac.compare_digest em Python, crypto.timingSafeEqual em Node, CryptographicOperations.FixedTimeEquals em C#) — comparação byte-a-byte vaza informação por análise de tempo.

Documentação completa​

Catálogo canônico (PT/EN/ES, 13 seções, 5 implementações de referência, troubleshooting, rotação de secret): https://github.com/nfe/shared-webhook-api/blob/main/docs/webhook-signature-validation.md

Não confunda autenticação com integridade

x-hub-signature (HMAC) autentica — prova que o webhook veio da NFE.io e não foi adulterado.

Content-MD5 apenas detecta corrupção em trânsito — qualquer atacante pode calcular o MD5 do payload forjado dele. MD5 ≠ autenticação.

Idempotência​

Webhooks podem chegar mais de uma vez após retry. Seu handler deve deduplicar usando (companyId, accessKey) ou (companyId, nsu) como chave única.

Boas práticas adicionais:

  • Responda rápido: retorne 2xx em até 5 segundos e processe assincronamente (fila interna).
  • Persista o payload antes de processar — não perca dados em crash do consumer.
  • Baixe XML/PDF cedo — URLs assinadas em links.xml expiram em 1 hora.
  • Monitore HTTP 5xx no seu endpoint — detecte problemas antes que o orçamento de retry esgote.

Veja também​

NFE.io

A NFE.io é uma empresa de tecnologia que fornece soluções para automatizar e simplificar a emissão e gestão de notas fiscais eletrônicas. Com suas ferramentas, as empresas podem economizar tempo e reduzir erros, aumentando a eficiência e precisão do processo de emissão de notas fiscais.

Um dos principais cases de sucesso da NFE.io é a implementação da solução na empresa de transporte Rodonaves. Com a automatização da emissão e gestão de notas fiscais eletrônicas, a Rodonaves conseguiu reduzir em até 80% o tempo gasto nesse processo, o que se traduziu em uma significativa melhoria na eficiência operacional. Além disso, a empresa também conseguiu eliminar erros e atrasos na emissão de notas fiscais, o que melhorou a relação com seus clientes e aumentou a confiança dos órgãos fiscais.

Outro exemplo é a implementação da NFE.io na empresa de comércio eletrônico, a Loja Integrada. Com a automatização da emissão de notas fiscais, a Loja Integrada conseguiu aumentar a velocidade de emissão de notas em até 10 vezes, o que permitiu que a empresa atendesse a uma maior quantidade de clientes e, consequentemente, aumentar as suas vendas.

Além desses exemplos, a NFE.io também tem outros cases de sucesso com empresas de setores como indústria, construção, varejo e serviços, mostrando a versatilidade e eficácia da sua solução.

Em resumo, a NFE.io é uma empresa de tecnologia que oferece soluções para automatizar e simplificar a emissão e gestão de notas fiscais eletrônicas, ajudando as empresas a economizar tempo e reduzir erros, melhorando a eficiência e precisão do processo. Com cases de sucesso em diferentes setores, a NFE.io tem se destacado como uma empresa líder em automação fiscal.