Payloads dos webhooks de documentos recebidos
Os eventos inbound notificam documentos emitidos por terceiros contra a sua empresa, capturados automaticamente pela NFE.io nos fiscos (SEFAZ para NF-e/CT-e e ambientes municipais/nacionais para NFS-e). Você não precisa consultar nada: recebe a notificação quando o documento aparece.
Para os webhooks das notas que você emite, veja Payloads de emissão.
Entregas reais de produção, com CNPJ e chaves de acesso substituídos por valores fictícios de dígito verificador válido. Estrutura, datas e códigos são fiéis ao original.
1. Tipos de evento
eventType | Documento | Quando dispara |
|---|---|---|
product_invoice_inbound | NF-e completa | NF-e autorizada contra seu CNPJ, com XML disponível |
product_invoice_inbound_summary | Resumo de NF-e | Só metadados — XML completo ainda não liberado |
transportation_invoice_inbound | CT-e | CT-e em que sua empresa é destinatário, tomador ou remetente |
service_invoice_inbound | NFS-e recebida | NFS-e capturada contra seu CNPJ |
Ações observadas: issued_successfully, event_raised_successfully, input_event_raised_successfully (manifestação do destinatário) e outbound_successfully.
A SEFAZ entrega parte dos documentos apenas como resumo. Nesse caso você recebe *_inbound_summary com metadados, sem XML completo. Para obter o documento inteiro, faça a manifestação do destinatário ou consulte o XML pela API.
2. Envelope: achatado na raiz
Diferente da NFS-e de emissão, todos os eventos inbound entregam os campos direto na raiz do corpo — sem {"payload": {...}}.
const chave = body.accessKey; // inbound: direto na raiz
3. CT-e recebido — transportation_invoice_inbound
{
"id": "495e19ab-65b5-9cae-8221-446ea118f37f",
"type": "transportationInvoice",
"accessKey": "29260739425723000178570010000012681998653070",
"parentAccessKey": "",
"nsu": 6381,
"direction": "Received",
"description": "Autorizado o uso do CT-e",
"issuedOn": "2026-07-27T14:54:00+00:00",
"createdOn": "2026-07-27T20:08:28.1671889Z",
"totalAmount": "100.00",
"company": {
"id": "5cdd21a911f635bd26a5fbe8139ccdc6",
"federalTaxNumber": "71400294000197"
},
"recipient": {
"federalTaxNumber": "71400294000197",
"name": "MODELO SERVICOS LTDA"
},
"sender": {
"federalTaxNumber": "01434044000192",
"name": "AMOSTRA VAREJO LTDA"
},
"taker": {
"federalTaxNumber": "71400294000197",
"name": "MODELO SERVICOS LTDA"
},
"dispatcher": {},
"productInvoices": [
{ "accessKey": "31260739425723000178550010011421491166299040" },
{ "accessKey": "31260739425723000178550010011421501170096915" }
],
"xmlUrl": "https://api.nfse.io/v2/companies/5cdd21a911f635bd26a5fbe8139ccdc6/inbound/29260739425723000178570010000012681998653070/xml"
}
Campos principais
| Campo | Tipo | Observação |
|---|---|---|
accessKey | string | Chave do CT-e (44 dígitos). Use como chave natural do documento. |
nsu | number | Número sequencial da SEFAZ. Atenção: aqui é número; em NF-e inbound costuma vir string. |
company | objeto | A sua empresa (a que recebeu). company.id é o id na NFE.io. |
recipient / sender / taker / dispatcher | objeto | Participantes do transporte. Podem vir vazios ({}) — veja §5. |
productInvoices[] | array | Chaves das NF-e transportadas por este CT-e. |
direction | string | Received para documentos recebidos. |
xmlUrl | string | URL autenticada do XML. Requer sua API key. |
productInvoices[].accessKey traz as chaves das NF-e cobertas pelo frete — é por aí que se casa o custo de transporte com os pedidos.
4. NF-e recebida — product_invoice_inbound
Mesmo envelope achatado, com participantes fiscais em vez de transporte:
{
"accessKey": "35260739425723000178550010011320981999999997",
"createdOn": "2026-05-19T11:41:44.695Z",
"parentAccessKey": "",
"company": {
"id": "5cdd21a911f635bd26a5fbe8139ccdc6",
"federalTaxNumber": "71400294000197"
},
"issuer": {
"federalTaxNumber": "01434044000192",
"name": "AMOSTRA VAREJO LTDA"
},
"buyer": {
"federalTaxNumber": "71400294000197",
"name": "MODELO SERVICOS LTDA"
},
"type": "productInvoice",
"nsu": "34691",
"nfeNumber": "1132098",
"nfeSerialNumber": "1",
"issuedOn": "2026-05-18T04:08:27Z",
"description": "Autorizado o uso da NF-e",
"totalInvoiceAmount": "980.72",
"operationType": "Incoming",
"links": {
"xml": "https://api.nfse.io/v2/companies/5cdd21a911f635bd26a5fbe8139ccdc6/inbound/35260739425723000178550010011320981999999997/xml",
"pdf": "https://api.nfse.io/v2/companies/5cdd21a911f635bd26a5fbe8139ccdc6/inbound/35260739425723000178550010011320981999999997/pdf"
}
}
Discrimine pelo par type + ação
O eventType fica na rota interna e não é propagado no corpo. Para saber o que chegou, use type (no corpo) com o cabeçalho X-Hook-Event:
type | Significa |
|---|---|
productInvoice | NF-e completa |
productInvoiceEvent | Evento de NF-e (cancelamento, CC-e, EPEC…) |
productInvoiceSummary | Resumo de NF-e |
productInvoiceEventSummary | Resumo de evento |
transportationInvoice | CT-e |
transportationInvoiceEvent | Evento de CT-e |
Eventos de NF-e (event_raised_successfully)
Quando o documento é um evento e não a nota:
accessKeytem 51 dígitos ({tpEvento}{chaveNFe}{sequência}), não 44.parentAccessKeytraz a chave da NF-e referenciada (44 dígitos).links.pdfvem string vazia — eventos não geram PDF.- Campos da nota-pai (
nfeNumber,issuer,buyer,totalInvoiceAmount) só vêm preenchidos se a NF-e já estiver indexada. O evento pode chegar antes do documento.
operationType pode enganar em eventosoperationType é sempre serializado, mas se a NF-e pai não estiver indexada o servidor não determina a operação real e emite Outgoing por padrão. Só confie nele quando os demais campos condicionais também vierem preenchidos.
5. Regras de serialização
- Campos nulos são omitidos. A chave não vem. Use acesso seguro.
- Objeto com todas as propriedades nulas vira
{}. No exemplo do CT-e,dispatcherchega vazio — o container existe, as chaves internas não. Não trate{}como erro. - Container nunca instanciado desaparece. Em resumos, o objeto pai não vem nem como
null. nsuvaria de tipo: número no CT-e, string na NF-e inbound. Normalize.- Datas com precisão variável (
.695Z,.1671889Z, ou sem fração).
Como nulos são omitidos, a ausência de um campo não significa "não existe" — significa "estava nulo neste documento". Ausência não distingue "não informado" de "não aplicável".
6. Como baixar o XML
Os campos xmlUrl / links.xml apontam para a API da NFE.io e exigem autenticação — não são links públicos.
curl -H "Authorization: $NFE_API_KEY" \
"https://api.nfse.io/v2/companies/{companyId}/inbound/{accessKey}/xml"
Se você recebeu um resumo, o XML completo só fica disponível após a manifestação do destinatário.
7. Checklist de integração
- Lê os campos da raiz (inbound nunca envelopa em
payload). - Discrimina pelo campo
typedo corpo, não peloeventType. - Trata
{}(objeto vazio) como participante não informado, sem quebrar. - Normaliza
nsupara string. - Aceita
accessKeyde 44 dígitos (documento) e 51 (evento de NF-e). - Trata resumos (
*_summary) como documento parcial, sem XML. - Deduplica por
X-Hook-Id— e poraccessKey+nsuno seu domínio. - Autentica ao baixar XML/PDF.
- Tolera evento que chega antes da nota-pai (campos condicionais vazios).
Próximos passos
- Payloads de emissão — NFS-e, NF-e e NFC-e que você emite.
- Dúvidas frequentes — validação de assinatura.
- IPs de origem — allowlist.