---
title: "DFe Inbound: Documentação para Desenvolvedores"
description: "Produto: DFe Inbound — Recepção de NF-e e CT-e"
source_url: https://nfe.io/docs/documentacao/distribuicao/dfe-inbound-documentacao-tecnica-desenvolvedores
last_updated: 2026-06-25
---

# 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](/documentacao/distribuicao/dfe-inbound-documentacao-funcional-clientes).

## Fontes autoritativas

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

| Artefato | Para quê | Conteúdo |
| --- | --- | --- |
| `openapi-inbound-api.yaml` (PT) / `.en.yaml` (EN) | Contrato da API | Schemas 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 manuais | 55 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 Markdown | Guia de integração | Visã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](#1-introdução)
2. [Guia de Primeiros Passos](#2-guia-de-primeiros-passos)
3. [Conceitos Fundamentais](#3-conceitos-fundamentais)
4. [Autenticação](#4-autenticação)
5. [URL Base e Ambientes](#5-url-base-e-ambientes)
6. [Ativando o Serviço de Inbound](#6-ativando-o-serviço-de-inbound)
7. [Endpoints de NF-e](#7-endpoints-de-nf-e)
8. [Endpoints de CT-e](#8-endpoints-de-ct-e)
9. [Listagem Paginada via OData (NF-e e CT-e)](#9-listagem-paginada-via-odata-nf-e-e-ct-e)
10. [Webhooks — Recebendo Notificações](#10-webhooks--recebendo-notificações)
11. [Referência de Tipos e Enums](#11-referência-de-tipos-e-enums)
12. [Tratamento de Erros](#12-tratamento-de-erros)
13. [Exemplos de Integração](#13-exemplos-de-integração)
14. [Perguntas Frequentes (FAQ)](#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:

```bash
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
# 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:

```bash
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"`:

```bash
# 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-21 | Tipo | Descrição |
|---|---|---|
| `55` | NF-e | Nota Fiscal Eletrônica |
| `57` | CT-e | Conhecimento de Transporte Eletrônico |
| `65` | NFC-e | Nota Fiscal de Consumidor |
| `67` | CT-eOS | CT-e para Outros Serviços |

```python
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

| MetadataResourceType | Significado |
|---|---|
| `productInvoice` | NF-e completa (XML disponível) |
| `productInvoiceEvent` | Evento de NF-e (cancelamento, ciência, etc.) |
| `productInvoiceSummary` | Resumo de NF-e (apenas metadados básicos) |
| `productInvoiceEventSummary` | Resumo de evento |
| `transportationInvoice` | CT-e completo |
| `transportationInvoiceEvent` | Evento 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`:

```http
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`:

```http
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
```

### Exemplo de Requisição com Autenticação

```bash
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

```http
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:**

| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `StartFromNsu` | `long` | Não | NSU inicial. `0` = busca desde o início. Use um NSU específico para sincronizar com histórico |
| `EnvironmentSEFAZ` | `string` | Sim | `"Production"` (produção SEFAZ) ou `"Test"` (homologação SEFAZ) |
| `EventType` | `string` | Não | Filtro de tipos de evento (códigos separados por vírgula). `null` = todos |
| `Schema` | `int` | Não | `0`=NF-e + Eventos, `1`=Somente NF-e, `2`=Somente Eventos |
| `AutomaticManifesting.MinutesToWaitAwarenessOperation` | `int` | Não | Minutos aguardados antes de auto-manifestar ciência. Mínimo: 5 |
| `WebhookVersion` | `int` | Não | Versão do formato de webhook. Use `2` (mais recente) |

**Resposta de sucesso (200):**

```json
{
  "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

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

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

### Verificar Configuração Atual

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

### Desativar Inbound

```http
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.

```http
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):**

```json
{
  "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:**

| Campo | Tipo | Descrição |
|---|---|---|
| `accessKey` | `string` | Chave de acesso 44 dígitos |
| `createdOn` | `DateTime` | Quando o documento entrou no sistema |
| `nsu` | `string` | Número Sequencial Único na SEFAZ |
| `nfeNumber` | `string` | Número da NF-e |
| `nfeSerialNumber` | `string` | Série da NF-e |
| `issuedOn` | `DateTime` | Data de emissão |
| `type` | `string` | `productInvoice`, `productInvoiceEvent`, `productInvoiceSummary` |
| `description` | `string` | Status da NF-e (ex: "Autorizado o uso da NF-e") |
| `totalInvoiceAmount` | `string` | Valor total em reais |
| `operationType` | `string` | `Incoming` (destinatário) ou `Outgoing` (emitente) |
| `issuer` | `object` | Dados do emitente (quem emitiu a NF-e) |
| `buyer` | `object` | Dados do destinatário (comprador) |
| `links.xml` | `string` | URL temporária para download do XML (expira em 1 hora) |
| `links.pdf` | `string` | URL temporária para download do PDF/DANFE |

### Baixar XML de uma NF-e

```http
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

```http
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.

```http
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.

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

**Tipos de manifestação (tpEvent):**

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

**Resposta (200):**

```json
"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.

```http
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](#6-ativando-o-serviço-de-inbound) — o processo é idêntico ao NF-e mas no endpoint `/transportationinvoices`.

### Buscar Metadados de um CT-e

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

**Resposta (200):**

```json
{
  "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:**

| Campo | Tipo | Descrição |
|---|---|---|
| `id` | `string` | Id gerado pela plataforma. |
| `accessKey` | `string` | Chave de acesso 44 dígitos do CT-e. |
| `parentAccessKey` | `string` | Vazio em respostas REST de CT-e (só populado em eventos). |
| `createdOn` | `DateTime` | Quando o documento entrou no sistema. |
| `nsu` | `long` | NSU. **Atenção: em CT-e o NSU é numérico** (em NF-e é string). |
| `type` | `string` | Sempre `"transportationInvoice"` neste endpoint. |
| `description` | `string` | Status fixo: `"Autorizado o uso do CT-e"`. |
| `company` | `object` | Sua empresa nfe.io (`id`, `federalTaxNumber`). |
| `issuedOn` | `DateTime` | Data de emissão do CT-e. |
| `xmlUrl` | `string` | URL 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

```http
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

```http
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:

```http
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:

```http
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/`:

| Endpoint | Retorna | Filtro obrigatório |
| --- | --- | --- |
| `ProductInvoices` | Listagem de NF-e da sua empresa | `environmentType eq {1 ou 2}` |
| `ProductInvoiceEvents` | Listagem de eventos de uma NF-e específica | `accessKey eq '{chave_44_dígitos}'` |
| `TransportationInvoices` | Listagem de CT-e da sua empresa | `issuedOn eq {data_ISO}` |
| `TransportationInvoiceEvents` | Listagem de eventos de CT-e | `receiptOn 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âmetro | Tipo | Obrigatório | Descrição |
| --- | --- | --- | --- |
| `$filter` | string | **Sim** | Filtro simples no formato `propriedade eq valor`. Veja restrições abaixo. |
| `$top` | int | Não | Quantidade máxima de registros por página. **Máximo: 1000, padrão: 50**. |
| `$skiptoken` | long | Não | Cursor de paginação — use o `nsu` do último item da página anterior. |
| `$count` | bool | Não | Se `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`

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

**Valores aceitos para `environmentType`:**

| Valor | Significado |
| --- | --- |
| `1` | Produção |
| `2` | Homologação (testes) |

**Resposta (200):**

```json
{
  "@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`

```http
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):**

```json
{
  "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`

```http
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):**

```json
{
  "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`

```http
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`):**

```bash
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`):**

```bash
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

| Erro | Causa | Como corrigir |
| --- | --- | --- |
| **400** `filter environmentType eq is required` | O `$filter` está ausente ou malformado | Adicione `$filter=environmentType eq 1` (ou `2`) na URL |
| **400** `filter accessKey eq is required` | Faltou o filtro por `accessKey` em `ProductInvoiceEvents` | Adicione `$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`/`or` | Tentativa de filtro composto | Use **apenas um** predicado simples por requisição |
| **401** `Unauthorized` | API Key ausente ou inválida | Verifique o header `Authorization` |
| **403** `Forbidden` | API Key sem a role correta | Confirme se a role inclui `Nota Fiscal (api.nfe.io)` ou a role específica do endpoint |
| **Página vazia** na primeira requisição | Não há dados no ambiente solicitado | Verifique 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](/documentacao/distribuicao/dfe-inbound-webhook-nfe-cte)**.
> 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

| Valor | Descrição |
|---|---|
| `"Test"` | Ambiente de homologação SEFAZ — documentos sem validade fiscal |
| `"Production"` | Ambiente de produção SEFAZ — documentos com validade fiscal real |

### OperationType

| Valor | Descrição |
|---|---|
| `"Incoming"` | Você é o destinatário (recebeu o documento) |
| `"Outgoing"` | Você é o emitente (emitiu o documento) |

### MetadataResourceType

| Valor | Descriçã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

| Valor | Descriçã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:

| Propriedade | Tipo | Descrição |
| --- | --- | --- |
| `relatedIds` | `string[]` | 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:**

```json
{
  "_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:

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

### Códigos HTTP

| HTTP | Significa | O que fazer |
| --- | --- | --- |
| `200` | Sucesso | Processe a resposta normalmente |
| `202` | Aceito (assíncrono) | Operação enfileirada — acompanhe via webhook ou GET correspondente |
| `204` | Sucesso sem conteúdo | Operação realizada, sem corpo de resposta |
| `302` | Redirect | Quando um endpoint redireciona para uma URL assinada, siga o header `Location` (a maioria dos clientes HTTP faz isso automaticamente) |
| `400` | Requisição inválida | Verifique os parâmetros enviados |
| `401` | Não autorizado | Verifique sua API Key ou token |
| `403` | Proibido | API Key sem a *policy* exigida (`NFeDist`/`CTeDist`) |
| `404` | Não encontrado | O documento/empresa não existe |
| `409` | Conflito (duplicado) | Recurso já existe |
| `422` | Entidade não processável | Dados válidos mas não processáveis (ex: NF-e cancelada) |
| `503` | Serviço indisponível | SEFAZ temporariamente fora. Tente novamente em alguns minutos |
| `504` | Timeout | A operação demorou muito. Tente novamente |
| `500` | Erro interno | Entre 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`:

| `tpEvento` | Nome | Quem emite | Quando aparece no inbound |
| --- | --- | --- | --- |
| `110110` | Carta de Correção (CC-e) | Emissor | Recebida automaticamente após autorização SEFAZ. |
| `110111` | Cancelamento | Emissor | Recebido automaticamente após autorização SEFAZ. |
| `210200` | Confirmação da Operação | Destinatário | Disparado por `POST /manifest?tpEvent=210200` ou pela política de manifestação automática. |
| `210210` | Ciência da Operação | Destinatário | **Default** de `POST /manifest`. |
| `210220` | Operação Não Realizada | Destinatário | Disparado manualmente — exige justificativa SEFAZ. |
| `210240` | Desconhecimento da Operação | Destinatário | Disparado 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:

| `cStat` | Significado | Ação |
| --- | --- | --- |
| `135` | Evento registrado e vinculado | Sucesso. |
| `136` | Evento registrado, não vinculado | Sucesso parcial — chave pode estar incorreta. |
| `573` | Duplicidade de evento | Já existe evento de mesmo tipo. |
| `217` | NF-e não consta na base | Aguarde alguns minutos (latência SEFAZ). |
| `999` | Erro interno SEFAZ | Retentar após 5–10 min. |

Para a lista completa de `cStat`, consulte o [Manual de Orientação do
Contribuinte da SEFAZ](https://www.nfe.fazenda.gov.br/portal/manualOrientacao.aspx).

---

## 13. Exemplos de Integração

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

```csharp
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)

```javascript
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)

```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
<?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.

```javascript
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)

```csharp
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](https://nfe.io/suporte).*
