Pular para o conteúdo principal

Documentação Técnica para Desenvolvedores — DFe Inbound (nfe.io)

Produto: DFe Inbound — Recepção de NF-e e CT-e Público: Desenvolvedores que integram sistemas com a API nfe.io Versão da API: v2 Atualizado em: 2026-05-07

📑 Este documento cobre os subsistemas NF-e e CT-e. Para o subsistema NFS-e Recebidas (captura via ADN — Ambiente de Dados Nacional), consulte:

  • 02-doc-tecnica-clientes-dev-nfse-inbound-api.md — referência completa de endpoints REST do NFS-e
  • 02-doc-tecnica-clientes-dev-nfse-inbound-webhook.md — contrato dos webhooks do NFS-e

Para a visão geral dos três subsistemas (NF-e, CT-e e NFS-e), abra README.md e 03-doc-funcional-clientes.md.

Fontes autoritativas

A API é descrita em três artefatos sincronizados. Em caso de divergência, o OpenAPI é a fonte canônica do contrato:

ArtefatoPara quêConteúdo
openapi-inbound-api.yaml (PT) / .en.yaml (EN)Contrato da APISchemas de request e response com example por schema, parâmetros, autenticação, todos os códigos de status (200/201/202/302/400/401/403/404/409/422/500). Importável em Swagger UI, Postman e geradores de cliente.
DFeDistribution-Inbound-API-Full.postman_collection.json (PT) / .en.postman_collection.json (EN)Testes manuais55 endpoints com request body de exemplo + response[] populado (200, 202, 400, 404 conforme aplicável). Visível na aba Examples de cada request no Postman.
Este MarkdownGuia de integraçãoVisão narrativa dos fluxos NF-e/CT-e, dicas de implementação, troubleshooting e exemplos em código (curl/Python/Node/C#).

Para testes manuais, importe a Postman collection acima — todos os 55 endpoints já trazem exemplo de resposta salvo (visível na aba Examples de cada request no Postman).


Índice

  1. Introdução
  2. Guia de Primeiros Passos
  3. Conceitos Fundamentais
  4. Autenticação
  5. URL Base e Ambientes
  6. Ativando o Serviço de Inbound
  7. Endpoints de NF-e
  8. Endpoints de CT-e
  9. Listagem Paginada via OData (NF-e e CT-e)
  10. Webhooks — Recebendo Notificações
  11. Referência de Tipos e Enums
  12. Tratamento de Erros
  13. Exemplos de Integração
  14. Perguntas Frequentes (FAQ)

1. Introdução

O DFe Inbound da nfe.io é um serviço que monitora automaticamente a SEFAZ e captura todos os Documentos Fiscais Eletrônicos (NF-e e CT-e) emitidos por terceiros que têm como destinatário o CNPJ da sua empresa.

O que você ganha com este serviço?

  • Recepção automática: Sem precisar acessar o portal da SEFAZ manualmente.
  • XML armazenado: Os XMLs ficam disponíveis para download via API a qualquer momento.
  • Notificações em tempo real: Receba webhooks assim que um documento chegar.
  • Dados estruturados: Consulte metadados (emitente, valor, data) sem precisar parsear XML.
  • Histórico completo: Acesso a documentos recebidos nos últimos 90 dias (ou desde o NSU configurado).

Visão Geral da Integração

SEFAZ (Ambiente Nacional)

│ (Polling automático via NSU)

nfe.io DFe Inbound

├──▶ Armazena XML no cloud storage
├──▶ Salva metadados (emitente, valor, data...)
└──▶ Dispara Webhook ──▶ SEU SISTEMA

├── Consulta metadados via API
└── Baixa XML via API

2. Guia de Primeiros Passos

Se você está integrando pela primeira vez, siga esta sequência em ordem. Não pule etapas.

Etapa 1 — Obter credenciais

No painel nfe.io, crie uma API Key em Configurações → Chaves de API. Guarde-a com segurança — ela só é exibida uma vez.

Etapa 2 — Localizar seu company_id

Em Empresas, clique na empresa que deseja integrar e copie o company_id exibido na URL ou nos detalhes da empresa.

Etapa 3 — Ativar o inbound

Faça uma requisição para ativar o monitoramento do CNPJ:

curl -X POST \
"https://api.nfse.io/v2/companies/SEU_COMPANY_ID/inbound/productinvoices" \
--header "Authorization: SUA_API_KEY" \
--header "Content-Type: application/json" \
-d '{
"StartFromNsu": 0,
"EnvironmentSEFAZ": "Production",
"WebhookVersion": 2
}'

Resposta esperada: { "status": "Active", ... }

Etapa 4 — Configurar seu endpoint de webhook

No painel nfe.io, vá em Webhooks e cadastre a URL do seu servidor que vai receber as notificações.

Mínimo necessário no seu endpoint:

# Python (Flask)
@app.route('/webhook', methods=['POST'])
def webhook():
data = request.json
access_key = data['accessKey']
# Processar...
return '', 200 # OBRIGATÓRIO retornar 200

Etapa 5 — Verificar que documentos chegam

Após ativar, aguarde entre 15 minutos e 4 horas. Consulte documentos recebidos:

curl "https://api.nfse.io/v2/companies/SEU_COMPANY_ID/inbound/productinvoices/CHAVE_44_DIGITOS" \
--header "Authorization: SUA_API_KEY"

Etapa 6 — Migrar para produção

Quando seus testes estiverem OK com "EnvironmentSEFAZ": "Test", recrie a configuração com "Production":

# 1. Desativar o inbound de homologação
curl -X DELETE \
"https://api.nfse.io/v2/companies/SEU_COMPANY_ID/inbound/productinvoices" \
--header "Authorization: SUA_API_KEY"

# 2. Ativar com ambiente de produção
curl -X POST \
"https://api.nfse.io/v2/companies/SEU_COMPANY_ID/inbound/productinvoices" \
--header "Authorization: SUA_API_KEY" \
--header "Content-Type: application/json" \
-d '{
"StartFromNsu": 0,
"EnvironmentSEFAZ": "Production",
"WebhookVersion": 2
}'

Importante: Documentos de ambiente Test (homologação SEFAZ) e Production são separados. Nunca misture os dois na mesma configuração.


3. Conceitos Fundamentais

NSU — Número Sequencial Único

O NSU é um número mantido pela SEFAZ que cresce a cada documento recebido pelo seu CNPJ. Pense nele como um "contador de correspondências" da SEFAZ para a sua empresa.

Exemplo:

  • NSU 1000: NF-e de fornecedor A, valor R$ 500
  • NSU 1001: NF-e de fornecedor B, valor R$ 1.200
  • NSU 1002: Evento de cancelamento da NF-e do fornecedor A

Nosso serviço consulta a SEFAZ periodicamente e baixa todos os documentos a partir do último NSU processado.

Chave de Acesso (access_key)

É um código de 44 dígitos que identifica unicamente uma NF-e ou CT-e. Exemplo:

35240112345678000195550010000012341234567890
│ │ │ │ │ │
│ │ │ │ │ └─── Dígito verificador
│ │ │ │ └────── Número NF-e
│ │ │ └─────────── Série
│ │ └────────────────────────── CNPJ emitente
│ └──────────────────────────── Mês/Ano emissão (AAAAMM)
└─────────────────────────────── UF (35 = SP)

Para eventos (cancelamento, ciência, etc.), a chave tem 55 dígitos.

Identificando o tipo de documento pela chave

A posição 20-21 da chave de acesso (0-indexed) indica o modelo do documento:

Posição 20-21TipoDescrição
55NF-eNota Fiscal Eletrônica
57CT-eConhecimento de Transporte Eletrônico
65NFC-eNota Fiscal de Consumidor
67CT-eOSCT-e para Outros Serviços
def get_document_type(access_key: str) -> str:
model = access_key[20:22] # posições 20 e 21
types = {"55": "NF-e", "57": "CT-e", "65": "NFC-e", "67": "CT-eOS"}
return types.get(model, "Desconhecido")

Tipos de Registro retornados pela API

MetadataResourceTypeSignificado
productInvoiceNF-e completa (XML disponível)
productInvoiceEventEvento de NF-e (cancelamento, ciência, etc.)
productInvoiceSummaryResumo de NF-e (apenas metadados básicos)
productInvoiceEventSummaryResumo de evento
transportationInvoiceCT-e completo
transportationInvoiceEventEvento de CT-e

4. Autenticação

Todas as requisições exigem autenticação. Suportamos dois métodos:

Método 1 — API Key (recomendado para integração server-to-server)

Passe a chave de API no header Authorization:

Authorization: SUA_API_KEY_AQUI

Onde obter a API Key: Painel nfe.io → Configurações → Chaves de API.

Método 2 — Bearer Token (JWT)

Use um token JWT gerado via https://id.nfe.io:

Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

Exemplo de Requisição com Autenticação

curl -X GET \
"https://api.nfse.io/v2/companies/comp_123/inbound/productinvoices" \
--header "Authorization: sk_live_abc123..."

5. URL Base e Ambientes

A API tem uma única URL base para todos os clientes:

https://api.nfse.io

Não existe um host separado para testes. Para emitir/receber documentos em ambiente de homologação da SEFAZ (sem validade fiscal), use o campo EnvironmentSEFAZ: "Test" no corpo da requisição de ativação do inbound — ver §6. O switch entre homologação e produção é feito por configuração, não trocando de host.

Estrutura da URL:

https://api.nfse.io/v2/companies/{company_id}/inbound/...

O company_id é o identificador da sua empresa na plataforma nfe.io (encontrado no Painel → Empresas).


6. Ativando o Serviço de Inbound

Antes de receber documentos, você precisa ativar o serviço de inbound para cada CNPJ que deseja monitorar.

Ativar Inbound de NF-e

POST /v2/companies/{company_id}/inbound/productinvoices
Authorization: {api_key}
Content-Type: application/json

{
"StartFromNsu": 0,
"EnvironmentSEFAZ": "Production",
"AutomaticManifesting": {
"MinutesToWaitAwarenessOperation": 60
},
"WebhookVersion": 2
}

Campos do corpo:

CampoTipoObrigatórioDescrição
StartFromNsulongNãoNSU inicial. 0 = busca desde o início. Use um NSU específico para sincronizar com histórico
EnvironmentSEFAZstringSim"Production" (produção SEFAZ) ou "Test" (homologação SEFAZ)
EventTypestringNãoFiltro de tipos de evento (códigos separados por vírgula). null = todos
SchemaintNão0=NF-e + Eventos, 1=Somente NF-e, 2=Somente Eventos
AutomaticManifesting.MinutesToWaitAwarenessOperationintNãoMinutos aguardados antes de auto-manifestar ciência. Mínimo: 5
WebhookVersionintNãoVersão do formato de webhook. Use 2 (mais recente)

Resposta de sucesso (200):

{
"companyId": "comp_123",
"environmentSEFAZ": "Production",
"startFromNsu": 0,
"startFromDate": "2024-01-01T00:00:00Z",
"status": "Active",
"createdOn": "2024-03-15T10:30:00Z"
}

Ativar Inbound de CT-e

POST /v2/companies/{company_id}/inbound/transportationinvoices
Authorization: {api_key}
Content-Type: application/json

{
"StartFromNsu": 0,
"EnvironmentSEFAZ": "Production"
}

Verificar Configuração Atual

GET /v2/companies/{company_id}/inbound/productinvoices
Authorization: {api_key}

Desativar Inbound

DELETE /v2/companies/{company_id}/inbound/productinvoices
Authorization: {api_key}

7. Endpoints de NF-e

Buscar Metadados de uma NF-e

Retorna os dados estruturados de uma NF-e pela sua chave de acesso.

GET /v2/companies/{company_id}/inbound/productinvoices/{access_key}
Authorization: {api_key}

Parâmetros de URL:

  • company_id: ID da sua empresa na nfe.io
  • access_key: Chave de acesso de 44 dígitos da NF-e

Resposta de sucesso (200):

{
"accessKey": "35240112345678000195550010000012341234567890",
"createdOn": "2024-03-15T14:22:10Z",
"nsu": "21825",
"nsuParent": null,
"nfeNumber": "1234",
"nfeSerialNumber": "1",
"issuedOn": "2024-03-15T10:00:00Z",
"type": "productInvoice",
"description": "Autorizado o uso da NF-e",
"totalInvoiceAmount": "1500.00",
"operationType": "Incoming",
"issuer": {
"federalTaxNumber": "12345678000195",
"name": "Fornecedor LTDA"
},
"buyer": {
"federalTaxNumber": "98765432000100",
"name": "Minha Empresa S.A."
},
"company": {
"id": "comp_123",
"federalTaxNumber": "98765432000100"
},
"links": {
"xml": "https://storage.nfe.io/temp/xml/...",
"pdf": "https://storage.nfe.io/temp/pdf/..."
}
}

Descrição dos campos:

CampoTipoDescrição
accessKeystringChave de acesso 44 dígitos
createdOnDateTimeQuando o documento entrou no sistema
nsustringNúmero Sequencial Único na SEFAZ
nfeNumberstringNúmero da NF-e
nfeSerialNumberstringSérie da NF-e
issuedOnDateTimeData de emissão
typestringproductInvoice, productInvoiceEvent, productInvoiceSummary
descriptionstringStatus da NF-e (ex: "Autorizado o uso da NF-e")
totalInvoiceAmountstringValor total em reais
operationTypestringIncoming (destinatário) ou Outgoing (emitente)
issuerobjectDados do emitente (quem emitiu a NF-e)
buyerobjectDados do destinatário (comprador)
links.xmlstringURL temporária para download do XML (expira em 1 hora)
links.pdfstringURL temporária para download do PDF/DANFE

Baixar XML de uma NF-e

GET /v2/companies/{company_id}/inbound/productinvoices/{access_key}/xml
Authorization: {api_key}

# Retorna o arquivo XML diretamente (Content-Type: application/xml)

Alternativamente, use a URL temporária retornada em links.xml para download direto do storage (sem precisar passar pela API). Esse link expira em 1 hora.

Baixar PDF (DANFE) de uma NF-e

GET /v2/companies/{company_id}/inbound/productinvoices/{access_key}/pdf
Authorization: {api_key}

# Retorna o arquivo PDF (Content-Type: application/pdf)

Buscar Dados de um Evento de NF-e

Eventos são ações sobre a NF-e: cancelamento, ciência, confirmação de operação, etc.

GET /v2/companies/{company_id}/inbound/productinvoices/{access_key}/events/{event_key}
Authorization: {api_key}

O event_key tem 55 dígitos (chave de acesso da NF-e + código do evento + sequência).

Registrar Manifestação

A manifestação é o processo pelo qual o destinatário comunica à SEFAZ que tem conhecimento da NF-e. É obrigatória para NF-es de entrada.

POST /v2/companies/{company_id}/inbound/{access_key}/manifest?tpEvent=210210
Authorization: {api_key}

Tipos de manifestação (tpEvent):

CódigoTipoDescrição
210210Ciência da Operação"Estou ciente desta NF-e" (não confirma recebimento físico)
210200Confirmação da Operação"Recebi a mercadoria conforme NF-e"
210220Desconhecimento da Operação"Não reconheço esta operação"
210240Operação não Realizada"A operação não foi concluída"

Resposta (200):

"Manifestação registrada com sucesso"

Manifestação Automática: Se você configurou AutomaticManifesting.MinutesToWaitAwarenessOperation, o sistema registrará "Ciência da Operação" automaticamente após esse intervalo. Você não precisa chamar este endpoint para ciência se tiver auto-manifestação ativa.

Reprocessar Webhook de uma NF-e

Use quando o webhook não foi entregue ou precisa ser reenviado.

POST /v2/companies/{company_id}/inbound/productinvoices/{access_key}/processwebhook
Authorization: {api_key}

# Ou por NSU:
POST /v2/companies/{company_id}/inbound/productinvoices/{nsu}/processwebhook

8. Endpoints de CT-e

Ativar e Configurar CT-e

Veja a seção Ativando o Serviço de Inbound — o processo é idêntico ao NF-e mas no endpoint /transportationinvoices.

Buscar Metadados de um CT-e

GET /v2/companies/{company_id}/inbound/{access_key}
Authorization: {api_key}

Resposta (200):

{
"id": "abc123",
"accessKey": "35240198765432000100570010000009871234567890",
"parentAccessKey": "",
"createdOn": "2024-03-15T14:22:10Z",
"nsu": 10042,
"type": "transportationInvoice",
"description": "Autorizado o uso do CT-e",
"company": {
"id": "comp_123",
"federalTaxNumber": "98765432000100"
},
"issuedOn": "2024-03-15T10:00:00Z",
"xmlUrl": "https://api.nfse.io/v2/companies/comp_123/inbound/35240198765432000100570010000009871234567890/xml"
}

Campos do CT-e:

CampoTipoDescrição
idstringId gerado pela plataforma.
accessKeystringChave de acesso 44 dígitos do CT-e.
parentAccessKeystringVazio em respostas REST de CT-e (só populado em eventos).
createdOnDateTimeQuando o documento entrou no sistema.
nsulongNSU. Atenção: em CT-e o NSU é numérico (em NF-e é string).
typestringSempre "transportationInvoice" neste endpoint.
descriptionstringStatus fixo: "Autorizado o uso do CT-e".
companyobjectSua empresa nfe.io (id, federalTaxNumber).
issuedOnDateTimeData de emissão do CT-e.
xmlUrlstringURL para download do XML via API (válida via GET .../xml).

Observação: dados de emitente (transportadora), destinatário, tomador, expedidor, recebedor e valor total não vêm nesta resposta REST. Esses campos só são entregues no payload do webhook de CT-e (transportationInvoice / issued_successfully). Para reprocessar o webhook e obter o payload completo, use o endpoint processwebhook documentado abaixo.

Baixar XML de um CT-e

GET /v2/companies/{company_id}/inbound/{access_key}/xml
Authorization: {api_key}

# Retorna o arquivo XML (Content-Type: application/xml)

Reprocessar Webhooks de CT-e

POST /v2/companies/{company_id}/inbound/transportationinvoices/reprocess/webhook
Authorization: {api_key}
Content-Type: application/json

{
"Key": "35240198765432000100570010000009871234567890",
"Date": "2024-03-15"
}

Reprocessar NSUs Específicos

Útil quando um NSU falhou no processamento:

PUT /v2/companies/{company_id}/inbound/transportationinvoices/reprocess/item
Authorization: {api_key}
Content-Type: application/json

[10042, 10043, 10044]

Consolidação de Batch

Consolida batches em um intervalo de NSU:

PUT /v2/companies/{company_id}/inbound/transportationinvoices/batch/consolidation
Authorization: {api_key}
Content-Type: application/json

{
"StartNSU": 10000,
"EndNSU": 10500
}

9. Listagem Paginada via OData (NF-e e CT-e)

Esses endpoints permitem listar documentos em lote com paginação cursor-based. São a forma mais eficiente de processar grandes volumes de documentos fiscais quando você precisa sincronizar um histórico completo ou fazer auditorias.

Existem quatro endpoints OData, todos sob o mesmo prefixo /v2/companies/{company_id}/inbound/odata/:

EndpointRetornaFiltro obrigatório
ProductInvoicesListagem de NF-e da sua empresaenvironmentType eq {1 ou 2}
ProductInvoiceEventsListagem de eventos de uma NF-e específicaaccessKey eq '{chave_44_dígitos}'
TransportationInvoicesListagem de CT-e da sua empresaissuedOn eq {data_ISO}
TransportationInvoiceEventsListagem de eventos de CT-ereceiptOn eq {data_ISO}

9.1. Autenticação e permissões

  • Endpoints ProductInvoices e ProductInvoiceEvents exigem role Nota Fiscal (api.nfe.io) ou NFeDist (dfe.nfe.io).
  • Endpoints TransportationInvoices e TransportationInvoiceEvents exigem role Nota Fiscal (api.nfe.io) ou CTeDist (dfe.nfe.io).

9.2. Parâmetros OData suportados

ParâmetroTipoObrigatórioDescrição
$filterstringSimFiltro simples no formato propriedade eq valor. Veja restrições abaixo.
$topintNãoQuantidade máxima de registros por página. Máximo: 1000, padrão: 50.
$skiptokenlongNãoCursor de paginação — use o nsu do último item da página anterior.
$countboolNãoSe true, inclui a contagem total na resposta.

Restrição importante sobre $filter: apenas expressões simples de igualdade (prop eq valor) são suportadas. Filtros compostos com and/or não funcionam — use um único predicado por requisição. Essa limitação existe para manter a performance dos índices MongoDB.

9.3. Paginação cursor-based via $skiptoken

A paginação funciona por cursor incremental usando o NSU (ascendente):

  1. Cliente faz a primeira requisição sem $skiptoken.
  2. API retorna até $top registros ordenados por nsu ascendente (do menor para o maior).
  3. Cliente pega o nsu do último item retornado.
  4. Cliente faz a próxima requisição passando esse valor em $skiptoken.
  5. API retorna os próximos registros com nsu maior que o $skiptoken.
  6. Quando a resposta vier com menos registros que $top (ou vazia), você chegou ao fim.

A ordem é sempre ascendente (menor NSU primeiro). Você sempre começa pelos documentos mais antigos e avança no tempo.

9.4. Listar NF-e — ProductInvoices

GET /v2/companies/{company_id}/inbound/odata/ProductInvoices?$filter=environmentType eq 1&$top=100
Authorization: {api_key}

Valores aceitos para environmentType:

ValorSignificado
1Produção
2Homologação (testes)

Resposta (200):

{
"@odata.context": "https://api.nfse.io/v2/companies/comp_123/inbound/odata/$metadata#ProductInvoices",
"value": [
{
"accessKey": "35240612345678000195550010000012341123456789",
"createdOn": "2026-04-09T10:30:00Z",
"company": {
"id": "comp_123",
"federalTaxNumber": "12345678000195"
},
"type": "productInvoice",
"nsu": 373289,
"environmentType": 1,
"issuedOn": "2026-04-08T14:00:00Z",
"nfeNumber": "1234",
"nfeSerialNumber": "1",
"operationType": "Incoming",
"totalInvoiceAmount": "1500.50",
"federalTaxNumberSender": "98765432000100",
"nameSender": "Fornecedor LTDA",
"xmlUrl": "https://api.nfse.io/v2/companies/comp_123/inbound/3524.../xml"
}
]
}

Atenção ao tipo de nsu: neste endpoint OData o nsu é long (número). Nos endpoints REST individuais (/productinvoices/{access_key}), o mesmo campo é string. Essa diferença é histórica e deve ser tratada no cliente conforme o contexto.

9.5. Listar Eventos de NF-e — ProductInvoiceEvents

GET /v2/companies/{company_id}/inbound/odata/ProductInvoiceEvents?$filter=accessKey eq '35240612345678000195550010000012341123456789'&$top=50
Authorization: {api_key}

O filtro é obrigatoriamente por accessKey — a chave de acesso de 44 dígitos da NF-e pai. O fluxo típico é:

  1. Listar NF-e via ProductInvoices (passo 9.4).
  2. Para cada NF-e retornada, chamar ProductInvoiceEvents com a accessKey dela para obter os eventos relacionados (ciência, confirmação, cancelamento, CC-e, etc.).

Resposta (200):

{
"value": [
{
"id": "comp_123_abc_35240612345678000195550010000012341123456789110111",
"createdOn": "2026-04-09T11:00:00Z",
"company": {
"id": "comp_123",
"federalTaxNumber": "12345678000195"
},
"accessKey": "35240612345678000195550010000012341123456789",
"type": "productInvoiceEvent",
"nsu": 373290,
"receiptOn": "2026-04-09T10:55:00Z",
"description": "Ciência da Operação",
"xmlUrl": "https://api.nfse.io/v2/companies/comp_123/inbound/3524.../xml"
}
]
}

9.6. Listar CT-e — TransportationInvoices

GET /v2/companies/{company_id}/inbound/odata/TransportationInvoices?$filter=issuedOn eq 2026-04-01&$top=100
Authorization: {api_key}

O filtro é obrigatoriamente pela data de emissão (issuedOn). Passe a data no formato yyyy-MM-dd (sem hora). A API retorna todos os CT-e emitidos exatamente naquele dia.

Resposta (200):

{
"value": [
{
"id": "comp_123_trans_35240198765432000100570010000009871234567890",
"createdOn": "2026-04-01T14:22:10Z",
"company": {
"id": "comp_123",
"federalTaxNumber": "98765432000100"
},
"accessKey": "35240198765432000100570010000009871234567890",
"parentAccessKey": "",
"type": "transportationInvoice",
"nsu": 10042,
"issuedOn": "2026-04-01T10:00:00Z",
"description": "Autorizado o uso do CT-e",
"xmlUrl": "https://api.nfse.io/v2/companies/comp_123/inbound/3524.../xml"
}
]
}

9.7. Listar Eventos de CT-e — TransportationInvoiceEvents

GET /v2/companies/{company_id}/inbound/odata/TransportationInvoiceEvents?$filter=receiptOn eq 2026-04-01&$top=100
Authorization: {api_key}

Similar ao endpoint de CT-e, mas o filtro é pela data de recebimento do evento (receiptOn), não pela data de emissão.

9.8. Exemplo completo de paginação (NF-e)

Primeira página (sem $skiptoken):

curl -G "https://api.nfse.io/v2/companies/comp_123/inbound/odata/ProductInvoices" \
--header "Authorization: sk_live_abc123" \
--data-urlencode '$filter=environmentType eq 1' \
--data-urlencode '$top=100'

Suponha que a resposta contenha 100 NF-e e o último item tenha nsu = 373388.

Próxima página (passando o último NSU como $skiptoken):

curl -G "https://api.nfse.io/v2/companies/comp_123/inbound/odata/ProductInvoices" \
--header "Authorization: sk_live_abc123" \
--data-urlencode '$filter=environmentType eq 1' \
--data-urlencode '$top=100' \
--data-urlencode '$skiptoken=373388'

Critério de parada: quando a resposta vier com value vazio ou com menos itens que $top, você chegou ao fim dos registros.

9.9. Erros comuns e troubleshooting

ErroCausaComo corrigir
400 filter environmentType eq is requiredO $filter está ausente ou malformadoAdicione $filter=environmentType eq 1 (ou 2) na URL
400 filter accessKey eq is requiredFaltou o filtro por accessKey em ProductInvoiceEventsAdicione $filter=accessKey eq '{chave_44}'
400 Could not find a property named 'X'Nome da propriedade no filtro está errado (case sensitive)Use exatamente environmentType, accessKey, issuedOn, receiptOn
400 The query specified in the URI is not valid com and/orTentativa de filtro compostoUse apenas um predicado simples por requisição
401 UnauthorizedAPI Key ausente ou inválidaVerifique o header Authorization
403 ForbiddenAPI Key sem a role corretaConfirme se a role inclui Nota Fiscal (api.nfe.io) ou a role específica do endpoint
Página vazia na primeira requisiçãoNão há dados no ambiente solicitadoVerifique se está consultando o ambiente correto (environmentType eq 1 vs 2)

10. Webhooks — Recebendo Notificações

📘 Doc dedicada: o contrato completo do webhook (envelope, payloads por evento, validação HMAC, reprocessamento) está em 02-doc-tecnica-clientes-dev-nfe-cte-inbound-webhook.md. Esta seção contém apenas o resumo essencial.

Como configurar

  1. Acesse o Painel nfe.io → Empresas → {sua empresa} → Webhooks.
  2. Cadastre a URL do seu endpoint HTTPS.
  3. Configure o Webhook Secret (usado para validação HMAC-SHA256).
  4. Ative o inbound (seção 6) com WebhookVersion: 2.

Famílias de evento entregues

Quando um documento ou evento é recebido, fazemos um POST para sua URL. O envelope traz body.type (um de seis valores: productInvoice, productInvoiceEvent, productInvoiceSummary, productInvoiceEventSummary, transportationInvoice, transportationInvoiceEvent) e body.action (issued_successfully, event_raised_successfully, input_event_raised_successfully). A tabela completa de pares body.type × body.action e o payload de cada um estão na §12 da doc dedicada acima.

Resposta esperada

  • Seu endpoint deve retornar HTTP 2xx em até 30 segundos.
  • Em caso de falha, retentamos até 50x em 24h com backoff exponencial.
  • Status 4xx (exceto 408/429) marca a entrega como DefinitivelyFailed (sem retry). Use POST .../processwebhook para reprocessar manualmente.

11. Referência de Tipos e Enums

EnvironmentSEFAZ

ValorDescrição
"Test"Ambiente de homologação SEFAZ — documentos sem validade fiscal
"Production"Ambiente de produção SEFAZ — documentos com validade fiscal real

OperationType

ValorDescrição
"Incoming"Você é o destinatário (recebeu o documento)
"Outgoing"Você é o emitente (emitiu o documento)

MetadataResourceType

ValorDescrição
"productInvoice"NF-e completa
"productInvoiceEvent"Evento de NF-e
"productInvoiceSummary"Resumo de NF-e (sem XML completo)
"productInvoiceEventSummary"Resumo de evento de NF-e
"transportationInvoice"CT-e
"transportationInvoiceEvent"Evento de CT-e

EntityStatus

ValorDescrição
"Active"Configuração ativa — sistema monitorando
"Inactive"Configuração inativa — sem monitoramento

Campo relatedIds em NFeMetadata

A collection NFeMetadata possui o campo relatedIds (array de strings) que armazena os IDs dos eventos relacionados àquela NF-e. Esse campo é populado automaticamente pelo worker DFeTech.App.Inbound.NFe sempre que um NFeEventMetadata é persistido:

PropriedadeTipoDescrição
relatedIdsstring[]IDs dos eventos ({accountId}_{companyId}_{eventId}) vinculados a esta NF-e

Semântica:

  • O link é implícito via accessKey — o worker identifica a NF-e pai pelo accessKey compartilhado entre NF-e e seus eventos
  • A operação de append é atômica e idempotente no MongoDB (usa $addToSet)
  • Se a NF-e pai ainda não existir quando o evento chegar (ordem de chegada não garantida pela SEFAZ), o evento é persistido normalmente e apenas um warning é logado — o link não é criado retroativamente nessa versão
  • Documentos antigos (persistidos antes desta implementação) podem não ter o campo relatedIds — o consumidor deve tratar ausência/null como array vazio

Exemplo de documento:

{
"_id": "acc-1_comp-1_35240612345678000195550010000012341123456789",
"accountId": "acc-1",
"companyId": "comp-1",
"accessKey": "35240612345678000195550010000012341123456789",
"nsu": 373289,
"environmentType": 1,
"relatedIds": [
"acc-1_comp-1_35240612345678000195550010000012341123456789110111",
"acc-1_comp-1_35240612345678000195550010000012341123456789110222"
]
}

12. Tratamento de Erros

Formato do Erro

Quando ocorre um erro, a API retorna um JSON no seguinte formato:

{
"errors": [
{
"code": 404,
"message": "Document not found for the given access key."
}
]
}

Códigos HTTP

HTTPSignificaO que fazer
200SucessoProcesse a resposta normalmente
202Aceito (assíncrono)Operação enfileirada — acompanhe via webhook ou GET correspondente
204Sucesso sem conteúdoOperação realizada, sem corpo de resposta
302RedirectQuando um endpoint redireciona para uma URL assinada, siga o header Location (a maioria dos clientes HTTP faz isso automaticamente)
400Requisição inválidaVerifique os parâmetros enviados
401Não autorizadoVerifique sua API Key ou token
403ProibidoAPI Key sem a policy exigida (NFeDist/CTeDist)
404Não encontradoO documento/empresa não existe
409Conflito (duplicado)Recurso já existe
422Entidade não processávelDados válidos mas não processáveis (ex: NF-e cancelada)
503Serviço indisponívelSEFAZ temporariamente fora. Tente novamente em alguns minutos
504TimeoutA operação demorou muito. Tente novamente
500Erro internoEntre em contato com o suporte

Formato do corpo de erro. Os controllers que herdam de ApiBaseController (a grande maioria) devolvem o wrapper ErrorsResource (JSON { "errors": [{ "code", "message" }] }) em qualquer 4xx/5xx — é o exemplo mostrado acima. Exceções (texto puro, não JSON):

  • BadRequest("...") direto — usado em validações de paginação e em alguns filtros OData (ex.: pageCount must not exceed 100).
  • NFSeDownloadController (endpoint público /blob/download) — sempre text/plain ("Invalid parameters.", "Link expired.", etc.).

Para o shape exato por endpoint e a lista completa de status documentados, consulte openapi-inbound-api.yaml.

Eventos NF-e (tpEvento) e códigos de retorno SEFAZ

Os eventos NF-e mais comuns retornados nos endpoints inbound e nas respostas de POST /manifest:

tpEventoNomeQuem emiteQuando aparece no inbound
110110Carta de Correção (CC-e)EmissorRecebida automaticamente após autorização SEFAZ.
110111CancelamentoEmissorRecebido automaticamente após autorização SEFAZ.
210200Confirmação da OperaçãoDestinatárioDisparado por POST /manifest?tpEvent=210200 ou pela política de manifestação automática.
210210Ciência da OperaçãoDestinatárioDefault de POST /manifest.
210220Operação Não RealizadaDestinatárioDisparado manualmente — exige justificativa SEFAZ.
210240Desconhecimento da OperaçãoDestinatárioDisparado manualmente quando a NF-e foi emitida indevidamente contra o CNPJ.

Códigos cStat de retorno SEFAZ mais frequentes na operação de manifestação:

cStatSignificadoAção
135Evento registrado e vinculadoSucesso.
136Evento registrado, não vinculadoSucesso parcial — chave pode estar incorreta.
573Duplicidade de eventoJá existe evento de mesmo tipo.
217NF-e não consta na baseAguarde alguns minutos (latência SEFAZ).
999Erro interno SEFAZRetentar após 5–10 min.

Para a lista completa de cStat, consulte o Manual de Orientação do Contribuinte da SEFAZ.


13. Exemplos de Integração

Exemplo 1 — Buscar NF-e por Chave de Acesso (C#)

using System.Net.Http;
using System.Text.Json;

public class NfeIoClient
{
private readonly HttpClient _http;
private readonly string _companyId;
private readonly string _baseUrl = "https://api.nfse.io";

public NfeIoClient(string apiKey, string companyId)
{
_companyId = companyId;
_http = new HttpClient();
_http.DefaultRequestHeaders.Add("Authorization", $"ApiKey {apiKey}");
}

public async Task<NfeMetadata> GetNfeAsync(string accessKey)
{
var url = $"{_baseUrl}/v2/companies/{_companyId}/inbound/productinvoices/{accessKey}";
var response = await _http.GetAsync(url);

response.EnsureSuccessStatusCode();

var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<NfeMetadata>(content);
}

public async Task<Stream> GetNfeXmlAsync(string accessKey)
{
var url = $"{_baseUrl}/v2/companies/{_companyId}/inbound/productinvoices/{accessKey}/xml";
return await _http.GetStreamAsync(url);
}
}

Exemplo 2 — Consumir Webhook e Baixar XML (Node.js)

const express = require('express');
const axios = require('axios');
const crypto = require('crypto');
const app = express();

app.use(express.raw({ type: 'application/json' })); // raw para validar assinatura

const API_KEY = 'sk_live_sua_chave_aqui';
const COMPANY_ID = 'comp_123';
const WEBHOOK_SECRET = 'seu_webhook_secret';
const BASE_URL = 'https://api.nfse.io';

function validateSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

app.post('/webhooks/nfeio', async (req, res) => {
// Validar assinatura
const signature = req.headers['x-nfe-signature'] || '';
if (!validateSignature(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Assinatura inválida');
}

const payload = JSON.parse(req.body);
const { event, accessKey, type, issuer, totalAmount } = payload;

console.log(`Novo documento: ${accessKey} - ${event}`);

try {
const xmlResponse = await axios.get(
`${BASE_URL}/v2/companies/${COMPANY_ID}/inbound/productinvoices/${accessKey}/xml`,
{ headers: { 'Authorization': `ApiKey ${API_KEY}` }, responseType: 'text' }
);

await saveDocumentToDatabase({ accessKey, type, issuer, totalAmount, xml: xmlResponse.data });

res.status(200).json({ status: 'ok' });
} catch (error) {
console.error('Erro:', error);
res.status(500).json({ error: error.message });
}
});

app.listen(3000);

Exemplo 3 — Listar CT-es do Último Mês com Paginação (Python)

import requests
from datetime import datetime, timedelta

API_KEY = "sk_live_sua_chave"
COMPANY_ID = "comp_123"
BASE_URL = "https://api.nfse.io"

def get_all_ctes_last_month():
"""Busca os CT-es do último mês.

A API não suporta filtro por intervalo de datas (`$filter` aceita apenas
igualdade simples — ver §9). A paginação é cursor-based via `$skiptoken`
(NSU). Portanto, percorremos as páginas em ordem de NSU e filtramos por
data no lado do cliente.
"""

cutoff = datetime.utcnow() - timedelta(days=30)
headers = {"Authorization": API_KEY}
all_ctes = []
skiptoken = None

while True:
url = (
f"{BASE_URL}/v2/companies/{COMPANY_ID}/inbound/odata/TransportationInvoices"
f"?$top=100"
)
if skiptoken is not None:
url += f"&$skiptoken={skiptoken}"

response = requests.get(url, headers=headers)
response.raise_for_status()
page = response.json().get("value", [])
if not page:
break

all_ctes.extend(
c for c in page
if datetime.fromisoformat(c["issuedOn"].replace("Z", "")) >= cutoff
)

# Cursor: NSU do último item desta página (ordenada por NSU ascendente).
skiptoken = page[-1]["nsu"]
print(f"Processados {len(all_ctes)} CT-es do período até agora...")

return all_ctes

ctes = get_all_ctes_last_month()
print(f"Total: {len(ctes)} CT-es")
for cte in ctes[:5]:
print(f" - {cte['accessKey']} | Emitido em: {cte['issuedOn']}")

Exemplo 4 — Ativar Inbound e Aguardar Primeiro Documento (PHP)

<?php

class NfeIoInbound
{
private string $apiKey;
private string $companyId;
private string $baseUrl = 'https://api.nfse.io';

public function __construct(string $apiKey, string $companyId)
{
$this->apiKey = $apiKey;
$this->companyId = $companyId;
}

private function makeRequest(string $method, string $path, ?array $body = null): array
{
$url = "{$this->baseUrl}/v2/companies/{$this->companyId}/{$path}";

$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
"Authorization: {$this->apiKey}",
"Content-Type: application/json"
],
]);

if ($body) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}

$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($statusCode >= 400) {
throw new RuntimeException("API Error {$statusCode}: {$response}");
}

return json_decode($response, true);
}

public function enableNfeInbound(int $startFromNsu = 0): array
{
return $this->makeRequest('POST', 'inbound/productinvoices', [
'StartFromNsu' => $startFromNsu,
'EnvironmentSEFAZ' => 'Production',
'WebhookVersion' => 2,
]);
}

public function getNfeMetadata(string $accessKey): array
{
return $this->makeRequest('GET', "inbound/productinvoices/{$accessKey}");
}
}

// Uso:
$client = new NfeIoInbound('sk_live_abc123', 'comp_123');
$config = $client->enableNfeInbound();
echo "Inbound ativado! Status: " . $config['status'];

Exemplo 5 — Listar todas as NF-e de produção com paginação OData (Node.js)

Este exemplo usa o endpoint ProductInvoices para iterar sobre todas as NF-e da sua empresa no ambiente de produção. Ideal para sincronização de histórico ou auditorias.

const axios = require('axios');

const API_KEY = 'sk_live_abc123';
const COMPANY_ID = 'comp_123';
const BASE_URL = 'https://api.nfse.io';
const PAGE_SIZE = 100;

const http = axios.create({
baseURL: BASE_URL,
headers: { Authorization: API_KEY }
});

async function listAllNFeProducao() {
const allDocuments = [];
let skipToken = null;
let pageNumber = 1;

while (true) {
const params = {
'$filter': 'environmentType eq 1', // 1 = Produção
'$top': PAGE_SIZE
};

if (skipToken !== null) {
params['$skiptoken'] = skipToken;
}

const response = await http.get(
`/v2/companies/${COMPANY_ID}/inbound/odata/ProductInvoices`,
{ params }
);

const items = response.data.value || [];
console.log(`Página ${pageNumber}: ${items.length} NF-e recebidas`);

if (items.length === 0) break;

allDocuments.push(...items);

// Critério de parada: última página veio com menos itens que o $top
if (items.length < PAGE_SIZE) break;

// Próximo cursor = NSU do último item da página atual
skipToken = items[items.length - 1].nsu;
pageNumber++;
}

return allDocuments;
}

// Para cada NF-e, buscar os eventos relacionados
async function listEventsForNFe(accessKey) {
const events = [];
let skipToken = null;

while (true) {
const params = {
'$filter': `accessKey eq '${accessKey}'`,
'$top': 50
};
if (skipToken !== null) params['$skiptoken'] = skipToken;

const response = await http.get(
`/v2/companies/${COMPANY_ID}/inbound/odata/ProductInvoiceEvents`,
{ params }
);

const items = response.data.value || [];
if (items.length === 0) break;

events.push(...items);
if (items.length < 50) break;

skipToken = items[items.length - 1].nsu;
}

return events;
}

// Execução
(async () => {
try {
const notas = await listAllNFeProducao();
console.log(`\nTotal de NF-e encontradas: ${notas.length}`);

// Exemplo: buscar eventos da primeira NF-e
if (notas.length > 0) {
const eventos = await listEventsForNFe(notas[0].accessKey);
console.log(`Eventos da NF-e ${notas[0].accessKey}: ${eventos.length}`);
}
} catch (err) {
console.error('Erro:', err.response?.data || err.message);
}
})();

Pontos importantes deste exemplo:

  • PAGE_SIZE está em 100 para equilibrar número de requisições e tamanho de resposta. Você pode subir até 1000.
  • O critério de parada é duplo: ou a resposta vem vazia, ou vem com menos itens que $top (indicando fim dos dados).
  • O $skiptoken sempre recebe o nsu do último item da página atual — nunca o primeiro.
  • A iteração é cursor-based, então é segura mesmo que novos documentos cheguem durante a execução (novos registros terão NSU maior e serão incluídos naturalmente).
  • Para buscar eventos de uma NF-e, use ProductInvoiceEvents passando a accessKey da NF-e pai.

Exemplo 6 — Listar NF-e paginado em C# (.NET)

using System.Net.Http.Headers;
using System.Text.Json;

var httpClient = new HttpClient { BaseAddress = new Uri("https://api.nfse.io") };
// A API Key é o valor direto do header Authorization (sem prefixo/esquema).
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "sk_live_abc123");

var companyId = "comp_123";
var pageSize = 100;
long? skipToken = null;
var allDocuments = new List<JsonElement>();

while (true)
{
var url = $"/v2/companies/{companyId}/inbound/odata/ProductInvoices" +
$"?$filter=environmentType eq 1&$top={pageSize}";

if (skipToken.HasValue)
url += $"&$skiptoken={skipToken.Value}";

var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();

var json = await response.Content.ReadAsStringAsync();
var doc = JsonDocument.Parse(json);
var items = doc.RootElement.GetProperty("value").EnumerateArray().ToList();

if (items.Count == 0) break;

allDocuments.AddRange(items);
Console.WriteLine($"Recebidas {items.Count} NF-e (total: {allDocuments.Count})");

if (items.Count < pageSize) break;

// Próximo cursor = NSU (long) do último item
skipToken = items.Last().GetProperty("nsu").GetInt64();
}

Console.WriteLine($"\nTotal final: {allDocuments.Count} NF-e");

14. Perguntas Frequentes (FAQ)

Quanto tempo leva para receber um documento após a emissão?

Em condições normais, entre 30 minutos e 4 horas após a autorização da SEFAZ. O nosso sistema faz polling periódico na SEFAZ; o intervalo depende da configuração, da sincronização entre a SEFAZ de origem e o Ambiente Nacional e da disponibilidade da SEFAZ.

O que é um "resumo" (productInvoiceSummary)?

A SEFAZ disponibiliza o productInvoiceSummary enquanto a NF-e ainda não tem uma manifestação do destinatário registrada — nesse estado, apenas os dados básicos da nota são liberados, sem o XML completo. O XML completo só é disponibilizado pela SEFAZ depois que o destinatário registra um evento de manifestação.

Para automatizar esse fluxo, o nosso sistema oferece a configuração AutomaticManifesting: ao receber um productInvoiceSummary, o destinatário pode ter a manifestação "Ciência da Operação" enviada automaticamente em seu nome. Assim que a SEFAZ processa essa manifestação, ela libera o XML completo, que é entregue por uma nova notificação (product_invoice_inbound / issued_successfully).

Posso buscar documentos históricos?

Sim. A SEFAZ mantém documentos disponíveis por até 90 dias para consulta via NSU. Configure StartFromNsu ao ativar o inbound para sincronizar o histórico.

A URL do XML expira?

Sim. As URLs retornadas em links.xml e links.pdf expiram em 1 hora. Para download, use a URL logo após obtê-la, ou chame o endpoint /xml a qualquer momento para obter uma nova URL.

O que acontece se meu webhook estiver fora do ar?

O sistema tentará reenviar o webhook com backoff exponencial por até 24 horas. Após esse período, você pode usar o endpoint de reprocessamento para solicitar novo envio.

Preciso manifestar todas as NF-es?

Pela legislação brasileira, o destinatário deve registrar uma das 4 manifestações para NF-es de entrada (que você está comprando): Ciência da Operação, Confirmação da Operação, Operação Não Realizada ou Desconhecimento da Operação. Não é obrigatório que seja a Ciência da Operação especificamente.

Para automatizar parte desse fluxo, o sistema oferece a configuração AutomaticManifesting, que envia a Ciência da Operação automaticamente em nome do destinatário. Essa manifestação foi escolhida para automação porque não impede que o destinatário, posteriormente, registre uma das demais manifestações (Confirmação, Operação Não Realizada ou Desconhecimento) caso o contexto fiscal exija.

Como testar sem afetar o ambiente de produção?

Configure com EnvironmentSEFAZ: "Test" para usar o ambiente de homologação SEFAZ. Documentos emitidos em homologação não têm validade fiscal. Ao terminar os testes, basta atualizar a configuração para "Production" — não é necessário deletá-la antes.

Como saber se o inbound está atrasado?

Consulte GET /productinvoices e compare OperationDetail_CurrentNsu com OperationDetail_MaxNsu. Se a diferença for grande e OperationDetail_ExecutedOn for antigo, pode haver um problema. Verifique se a configuração está Active e se o certificado digital não expirou.


Documentação mantida pela equipe de desenvolvimento nfe.io. Para reportar problemas ou solicitar funcionalidades, acesse o suporte em nfe.io/suporte.

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.