Pular para o conteúdo principal

Payloads dos webhooks de emissão

Esta página documenta o que chega no corpo da requisição quando a NFE.io notifica seu endpoint sobre uma emissão. Para o cadastro do webhook, veja Como cadastrar; para o conceito geral, Conceitos.

Fonte dos exemplos

Os exemplos desta página são entregas reais de produção, com dados sensíveis substituídos. CNPJ, CPF e chaves de acesso foram trocados por valores fictícios com dígito verificador válido — servem para testar parsing e validação de DV, mas não correspondem a documentos existentes. Valores monetários, alíquotas, códigos fiscais, datas e a estrutura são fiéis ao original.

1. A regra que quebra integrações: dois envelopes

O corpo entregue não tem a mesma forma para todos os produtos. Esta é a diferença mais importante desta página:

Tipo de eventoEnvelopeOnde ficam os campos
service_invoice (NFS-e){"payload": { ... }}dentro de payload
product_invoice (NF-e)achatadona raiz do corpo
consumer_invoice (NFC-e)achatadona raiz do corpo
*_inbound, product_tax, tax_payment_formachatadona raiz do corpo

Ou seja: para NFS-e você acessa body.payload.status; para NF-e/NFC-e, body.status.

Escreva o parser tolerante aos dois formatos

Se você assina mais de um tipo de evento no mesmo endpoint, normalize antes de processar:

// Funciona para NFS-e (envelopado) e NF-e/NFC-e (achatado)
const documento = body.payload ?? body;

Fazer isso desde o início evita retrabalho quando você passar a emitir outro tipo de documento.

2. Como descobrir qual evento chegou

Combine o cabeçalho com o corpo — os dois carregam informações diferentes:

OrigemO que trazExemplo
Cabeçalho X-Hook-Evento tipo do eventoservice_invoice
Cabeçalho X-Hook-Ididentificador único da entrega (use para deduplicar)4efa03baf6014b788e591da82efbaba8
Cabeçalho X-Hook-Attemptsnúmero da tentativa1
Corpo (flowStatus / status)o resultado da operaçãoIssued, IssueFailed, Error
Não confie no corpo para saber o tipo

X-Hook-Event traz o tipo do evento (service_invoice), não o par tipo + ação. Para saber o que aconteceu, leia flowStatus (NFS-e) ou status (NF-e/NFC-e) no corpo.

3. NFS-e — service_invoice

Envelope: {"payload": { ... }}. Terminologia própria: o emissor é provider e o tomador é borrower.

3.1. Emissão bem-sucedida

{
"payload": {
"id": "6a6898a87864720001efa2dc",
"externalId": "e671beec-254b-4952-a046-430df91b2d2f",
"environment": "Production",
"flowStatus": "Issued",
"status": "Issued",
"provider": {
"name": "EMPRESA EXEMPLO LTDA",
"federalTaxNumber": "09505320001905",
"municipalTaxNumber": "111111111",
"address": {
"postalCode": "20040020",
"street": "Rua Exemplo",
"number": "100",
"district": "Centro",
"city": { "code": "3304557", "name": "Rio de Janeiro" },
"state": "RJ",
"country": "BRA"
},
"type": "LegalPerson, Company"
},
"borrower": {
"name": "EXEMPLO DISTRIBUIDORA SA",
"federalTaxNumber": "34332984000120",
"address": {
"postalCode": "74915-240",
"city": { "code": "5201405", "name": "Aparecida de Goiânia" },
"state": "GO",
"country": "BRA"
},
"type": "LegalPerson"
},
"number": 2905,
"rpsNumber": 68521,
"rpsSerialNumber": "1",
"rpsType": "Rps",
"rpsStatus": "Normal",
"taxationType": "WithinCity",
"cityServiceCode": "002",
"federalServiceCode": "170102",
"nbsCode": "118061000",
"servicesAmount": 1620.0,
"baseTaxAmount": 1620.0,
"issRate": 0.05,
"issTaxAmount": 81.0,
"pisAmountWithheld": 10.53,
"cofinsAmountWithheld": 48.6,
"irAmountWithheld": 24.3,
"csllAmountWithheld": 16.2,
"inssAmountWithheld": 0.0,
"issAmountWithheld": 0.0,
"retentionType": "notWithheld",
"amountWithheld": 99.63,
"amountNet": 1520.37,
"issuedOn": "2026-07-28T08:55:20-03:00",
"createdOn": "2026-07-28T11:55:20.9045977+00:00",
"apiVersion": 2
}
}

Campos que a maioria das integrações usa:

CampoTipoPara que serve
idstringIdentificador da nota na NFE.io. Use para consultar via API.
externalIdstringO seu identificador, informado na emissão. Melhor chave para casar com o seu pedido.
flowStatusstringEstado do fluxo: Issued, IssueFailed, Cancelled
statusstringEstado da nota: Issued, Error, Cancelled.
environmentstringProduction ou Development. Sempre confira antes de gravar em produção.
numbernumberNúmero da NFS-e. 0 quando a emissão falhou.
rpsNumber / rpsSerialNumbernumber / stringNúmero e série do RPS.
amountNetnumberValor líquido após retenções.
documentUrl e documentXmlUrl não são links públicos

Quando presentes, esses campos podem vir com o esquema interno b2://, que não é acessível pelo seu sistema. Para obter PDF e XML, use os endpoints da API REST da nota.

3.2. Falha na emissão

Mesmo envelope; o que muda é o par flowStatus + flowMessage:

{
"payload": {
"id": "6a636c5a7864720001d754b3",
"externalId": "CUSTOMER_MONTHLY_SERVICE_FEE#CUSTOMER#00000000-0000-4000-8000-000000000000",
"environment": "Development",
"flowStatus": "IssueFailed",
"flowMessage": "max retry reached on send batch stage",
"status": "Error",
"number": 0,
"rpsNumber": 2190,
"rpsSerialNumber": "IO",
"cityServiceCode": "5895",
"federalServiceCode": "15.10",
"servicesAmount": 9.18,
"issRate": 0.02,
"issTaxAmount": 0.1836,
"amountNet": 9.18,
"approximateTax": {
"source": "IBPT/empresometro.com.br",
"version": "21.1.F",
"totalRate": 0.1829,
"totalAmount": 1.679022
},
"apiVersion": 2
}
}

Note que number vem 0 e status vem Error. A causa legível fica em flowMessage — registre esse campo no seu log, é o que o suporte pede primeiro.

4. NF-e — product_invoice

Envelope achatado. Terminologia: issuer (emitente) e buyer (destinatário).

{
"id": "3d4c1b2a5f6e7d8c9b0a1f2e3d4c5b6a",
"serie": 8,
"number": 25969,
"status": "Issued",
"authorization": {
"accessKey": "35260739425723000178550080000259691818677728"
},
"operationNature": "Outras Entradas - Retorno Simbólico",
"operationType": "Incoming",
"environmentType": "Production",
"purposeType": "Normal",
"issuer": {
"name": "MODELO SERVICOS LTDA",
"federalTaxNumber": 31305761000185,
"taxRegime": "LucroReal",
"address": {
"postalCode": "16204393",
"city": { "code": "3506508", "name": "BIRIGUI" },
"state": "SP",
"country": "BRA"
},
"type": "LegalEntity"
},
"buyer": {
"name": "EXEMPLO DISTRIBUIDORA SA",
"federalTaxNumber": 32308042000180,
"stateTaxNumberIndicator": "TaxPayer",
"address": {
"city": { "code": "3518800", "name": "Guarulhos" },
"state": "SP",
"country": "BRA"
},
"type": "LegalEntity"
},
"totals": {
"icms": {
"baseTax": 78.56,
"icmsAmount": 14.14,
"productAmount": 78.56,
"invoiceAmount": 78.56
},
"ibsCbs": {
"basis": 64.42,
"ibs": {
"state": { "amount": 0.06 },
"municipal": { "amount": 0.00 },
"totalAmount": 0.06
},
"cbs": { "amount": 0.58 }
}
},
"transport": { "freightModality": "Free" },
"payment": [
{ "paymentDetail": [ { "method": "WithoutPayment", "amount": 0 } ] }
],
"lastEvents": {
"events": [
{
"type": "Authorized",
"sequence": 5,
"data": {
"accessKey": "35260739425723000178550080000259691818677728",
"description": "Autorizado o uso da NF-e",
"protocolNumber": "135262973194611",
"statusCode": 100,
"environmentType": "Production"
}
},
{
"type": "WebHooksDispatched",
"sequence": 8,
"data": { "action": "product_invoice.issued_successfully" }
}
],
"hasMore": false
},
"apiVersion": 2
}

4.1. Onde está a chave de acesso

A accessKey aparece em dois lugares e ambos são válidos:

  1. authorization.accessKey — o caminho direto, prefira este.
  2. lastEvents.events[] no item de type: "Authorized", em data.accessKey.
const chave =
body.authorization?.accessKey ??
body.lastEvents?.events?.find(e => e.type === "Authorized")?.data?.accessKey;

4.2. lastEvents é o histórico da nota

O array traz o caminho percorrido, cada item com type e sequence (ordem crescente de acontecimento). Tipos observados em produção:

typeSignificado
DefinedNumberAndSerieSuccessfullyNúmero e série atribuídos
InvoiceSetAccessKeyChave de acesso calculada
InvoiceXmlSignedXML assinado
AuthorizedAutorizada pela SEFAZ — traz protocolNumber e statusCode
MergedXML final consolidado
SendSignedBatchFailedFalha no envio do lote (transiente; a nota pode seguir e autorizar)
WebHooksDispatchedRegistro do próprio disparo de webhook
SendSignedBatchFailed no histórico não significa nota com erro

É comum ver essa entrada em notas autorizadas com sucesso — houve uma falha transiente e o reenvio funcionou. Considere sempre o status da raiz, não a presença de um evento de falha no histórico.

5. NFC-e — consumer_invoice

Mesma estrutura da NF-e (envelope achatado, issuer/buyer, totals, lastEvents). A diferença prática é o destinatário pessoa física:

{
"id": "8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d",
"serie": 2,
"number": 1572,
"status": "Issued",
"authorization": {
"accessKey": "51260771924245000153650020000015721152114412"
},
"operationNature": "Venda de mercadorias",
"operationType": "Outgoing",
"environmentType": "Production",
"issuer": {
"name": "TESTE LOGISTICA SA",
"federalTaxNumber": 71924245000153,
"taxRegime": "SimplesNacional",
"type": "LegalEntity"
},
"buyer": {
"name": "Maria Silva",
"federalTaxNumber": "50465923046",
"email": "[email protected]",
"stateTaxNumberIndicator": "NonTaxPayer",
"address": {
"postalCode": "78360000",
"city": { "code": "5102637", "name": "Campo Novo do Parecis" },
"state": "MT",
"country": "BRA",
"phone": "11999999999"
},
"type": "NaturalPerson"
},
"totals": {
"icms": {
"productAmount": 22.9,
"freightAmount": 7.5,
"discountAmount": 5.72,
"othersAmount": 0.99,
"invoiceAmount": 25.67
}
},
"payment": [
{
"paymentDetail": [
{ "method": "Others", "methodDescription": "Marketplace Online", "amount": 25.67 }
],
"payBack": 0
}
],
"apiVersion": 2
}
O corpo pode conter dados pessoais

Em NFC-e o buyer costuma ser type: "NaturalPerson", com CPF, e-mail e telefone. Trate o corpo como dado pessoal: não registre em log aberto, restrinja acesso e observe sua política de retenção (LGPD).

6. Regras de serialização que afetam seu parser

Comportamentos reais do serializador — considere todos ao escrever o código:

  1. Campos nulos são omitidos. A chave simplesmente não vem. Nunca use "chave existe" como sinal; use acesso seguro (?., .get()).
  2. O tipo de federalTaxNumber varia. Em NF-e/NFC-e costuma vir número (31305761000185); em NFS-e, string. Normalize para string antes de comparar, e cuidado com zero à esquerda: um CNPJ que começa com 0 perde o dígito se tratado como inteiro.
  3. number também varia entre string e inteiro conforme o produto.
  4. Datas em ISO 8601 com offset, às vezes com milissegundos (2026-07-28T11:55:20.9045977+00:00). Não presuma resolução de segundos.
  5. Enums chegam como string ("Production"), não como número.
  6. Campos novos podem aparecer sem aviso. Ignore desconhecidos em vez de falhar — os campos ibsCbs da reforma tributária, por exemplo, foram adicionados a payloads já existentes.
  7. Não presuma ordem de chaves.

7. Sucesso da entrega ≠ sucesso fiscal

O erro mais comum nesta integração:

PerguntaOnde responder
"Recebi a notificação?"status HTTP que você retorna
"A nota foi emitida?"flowStatus / status no corpo

Um evento issued_failed é uma entrega bem-sucedida de uma notícia ruim: responda 2xx, e registre a falha internamente pelo flowMessage.

Não responda 5xx porque a nota deu erro

Retornar erro HTTP faz a NFE.io reenviar o mesmo evento repetidamente. Responda 2xx ao receber e trate o estado fiscal no seu sistema.

8. Checklist de integração

  • Normaliza o envelope (body.payload ?? body) para aceitar NFS-e e NF-e/NFC-e.
  • Valida a assinatura HMAC antes de processar — veja Dúvidas frequentes.
  • Deduplica por X-Hook-Id (reentrega é esperada).
  • Responde 2xx rápido e processa de forma assíncrona.
  • Trata _successfully, _failed e _error.
  • Confere environment / environmentType antes de gravar em produção.
  • Acesso seguro a todo campo opcional (nulos são omitidos).
  • Normaliza federalTaxNumber e number para string.
  • Ignora campos desconhecidos.
  • Não registra CPF/e-mail/telefone de NFC-e em log aberto.

Próximos passos

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.