# NFER API — guia para agentes de IA

Você está implementando integração com a **NFER**, API REST brasileira de emissão de NF-e (modelo 55) e NFC-e (modelo 65), leiaute 4.00.

- **Base URL produção:** `https://api.nfer.me/v1`
- **Auth:** header `X-API-Key: <company-key>` para emitir. Org key `nfer_org_…` só gestão de conta. Painel: `Authorization: Bearer <JWT>` via `POST /v1/auth/login` (account + companyId).
- **Formato company key:** `<8-chars-uuid>.<segredo>`. **1 key por CNPJ.** Conta agrupa N CNPJs (limites por plano).
- **Multi-CNPJ:** `POST /v1/accounts`, `POST /v1/accounts/:id/companies`. Compat: `POST /v1/companies` com `partner`+`partnerRef` find-or-create conta.
- **Docs HTML:** https://nfer.me/docs · https://nfer.me/docs/nfe/endpoints
- **Este arquivo:** https://nfer.me/llms-full.txt
- **Deep link parceiro:** https://nfer.me/login?tab=apikey&companyId=<UUID> (entra direto no CNPJ)

## Fluxo padrão (obrigatório)

```
1. POST /v1/companies              (público) → guardar apiKey (só uma vez)
2. POST /v1/companies/:id/certificate   (A1)
3. POST /v1/nfe                    → 201 rascunho (número reservado)
     Destinatário: customerId **ou** customer inline (upsert por CPF/CNPJ, não duplica)
4. POST /v1/nfe/:id/send           → 202 + jobId (fila assíncrona)
5. GET  /v1/nfe/:id                → poll até sair de processando (fallback)
6. GET  /v1/nfe/:id/xml|danfe
   XML no storage: `{companyId}/xml/{chave}.xml` (lookup por chave).
   Export diário/mensal: filtre `GET /v1/nfe?from&to` e monte pastas de dia só no ZIP.
```

Cadastro separado `POST /v1/customers` é opcional se o ERP mandar `customer` no create da NF-e.

**O que o ERP manda vs o que o NFER resolve:** destinatário + itens (descrição, NCM, qtd, valor) + pagamentos. Smart: `fiscalProfileId` (e/ou `productId`) → CFOP, CSOSN/CST, PIS, COFINS. Automático: `cMun` (UF+cidade ou CEP), `indFinal=1` se o dest. não tiver IE, IBPT no rodapé se `indFinal=1`, SVC se a SEFAZ cair. GET /nfe devolve snake_case; POST/PUT usam camelCase.

**indPres (default 9):** 1=balcão, 2=e-commerce no site próprio, 3=telefone, 4=delivery NFC-e, 9=WhatsApp/e-mail/outros. No XML o NFER envia `indIntermed=0` (venda direta) — evita cStat 434 em operação não presencial.

**Erro SEFAZ → campo do JSON → PUT + /send** no mesmo id. Não crie outra nota. Docs: /docs/nfe/endpoints e /docs/nfe/erros.

**Webhooks (recomendado):** uma URL HTTPS por empresa via `POST /v1/webhooks` (ou painel). Eventos: `nfe.autorizada`, `nfe.erro`, `nfe.denegada`, `nfe.cancelada`, `nfe.contingencia` (SVC/offline/EPEC), `nfe.batch.completed` (fim de lote). Campo `secret` opcional: sem secret não há `X-NFER-Signature`; com secret, HMAC `sha256=` sobre `{timestamp}.{rawBody}` (ver `X-NFER-Timestamp`). Polling em `GET /v1/nfe/:id` só como fallback — a cada **3–5 s** enquanto `processando`; não menos que 3 s (rate limit da API → `429`). Retry de `/send` reutiliza o **mesmo** número. Entrega: 15s timeout; até 8 tentativas (imediata → 10s → 30s → 1m → 5m → 15m → 30m → 2h). Teste: `POST /v1/webhooks/:id/test` body `{ "eventType", "nfeId" }` — payload real da nota + `data.test: true`; `data.status` segue o evento escolhido.

**Rate limit por plano (req/min):** Start 120, Growth 240, Business 480, Enterprise 900. Janela de 60s por empresa, com headers `X-RateLimit-Limit`, `X-RateLimit-Remaining` e `X-RateLimit-Reset`. Em `429`, respeite `Retry-After` e reaplique com backoff.

Payload JSON camelCase (não é snake_case do GET /nfe):

```json
{
  "id": "uuid-do-evento",
  "type": "nfe.autorizada",
  "createdAt": "2026-08-20T12:04:01.000Z",
  "data": {
    "nfeId": "uuid-da-nota",
    "numero": 8,
    "serie": 2,
    "status": "autorizada",
    "chaveAcesso": "44 dígitos ou null",
    "protocolo": "string ou null",
    "erroCstat": "string ou null",
    "erroMotivo": "string ou null",
    "modelo": "55",
    "environment": "homologacao"
  }
}
```

Em `nfe.erro` / `nfe.denegada`: `status` = `erro` ou `denegada` + `erroCstat` / `erroMotivo`. Teste manual inclui `data.test: true`.

Use `data.chaveAcesso` + `data.status` no ERP. XML/DANFE: `GET /v1/nfe/:id/xml` e `/danfe` depois de `autorizada`.

## Certificado A1 — avisos

Varredura diária do vencimento. Com SMTP ativo, e-mail ao fiscal (ou e-mail da empresa) nos marcos 30/15/7/3/1 dia, no vencimento e enquanto expirado. Mesmo aviso no sino do painel e banner no dashboard (Configurações → Notificações).

## Contingência SVC automática (NF-e 55)

Se a SEFAZ da UF falhar (cStat 108/109/584 ou timeout), o NFER **reautoriza sozinho** na SVC (SVC-AN ou SVC-RS conforme a UF), no mesmo job de `/send`. O ERP **não** escolhe contingência no payload.

| Campo | Em SVC |
|-------|--------|
| `numero` / `serie` | **Não mudam** |
| `chaveAcesso` | **Pode mudar** (tpEmis 6 ou 7 entra na chave de 44 dígitos) |
| Validade | Nota **já autorizada** na SVC — pode enviar ao cliente |
| Depois | Não vem outro número nem “segunda chave”. Cancel/CC-e usam essa chave |

O NFER sai da SVC quando: autoriza de novo no regime normal, o worker `contingency-probe` vê `statusServico` cStat 107 (~a cada 15 min), ou TTL Redis 24h.

**Regra do integrador:** nunca calcule chave no ERP antes do retorno. Espere `nfe.autorizada` (ou `GET` com `autorizada`) e use `chaveAcesso` + XML/DANFE oficiais. Não envie ao cliente nada em `processando`.

NFC-e 65: **não** usa SVC. Se a SEFAZ cair (timeout / cStat 108/109/584), emite contingência offline (`tpEmis=9`) — padrão do MOC. Status `contingencia`, cupom PDF imprimível, XML assinado guardado; o probe retransmite depois. Webhook `nfe.contingencia`.

EPEC (`tpEmis=4`) na NFC-e é **opcional / a critério da UF** (senão rejeição 714). O NFER não ativa EPEC automaticamente na NFC-e.

NF-e 55: se a **SVC também falhar** (transporte), o NFER registra EPEC formal (evento 110140 no Ambiente Nacional), status `contingencia`, `tpEmis=4`, webhook `nfe.contingencia`; o probe reautoriza depois. SVC continua sendo o caminho automático primário.

## Emissão em lote

`POST /v1/nfe/batch` — até **50** itens (mesmo body de `POST /nfe` ou `POST /nfce` conforme `modelo` `"55"`|`"65"`). Resposta **202** com `batchId`, `queued`, `items[]`. Poll: `GET /v1/nfe/batch/:batchId`. Status do lote: `processing` | `completed` | `partial` | `failed`. Webhook `nfe.batch.completed` quando o lote fecha. `send: false` só cria rascunhos.

## Convenções

| Camada | Estilo | Exemplos |
|--------|--------|----------|
| Request body | camelCase | razaoSocial, customerId, valorUnitario, cpfCnpj |
| POST /v1/companies response | camelCase | apiKey, razaoSocial |
| Demais respostas DB | snake_case | razao_social, chave_acesso, erro_cstat |
| Endereço | chaves fiscais | cMun, xMun, UF, CEP |

## Regras fiscais críticas (rejeição SEFAZ)

- `POST /v1/nfe` destinatário: `customerId` **ou** `customer` (inline). Os dois juntos: prevalece `customerId`. Inline faz **upsert** por CPF/CNPJ da empresa — atualiza o existente, não duplica.
- `endereco.cMun` (IBGE 7 dígitos) é obrigatório no **XML**. No payload é **opcional** se houver `UF`+`xMun` (tabela IBGE local) ou CEP (ViaCEP só no cadastro). Sem resolução → 400. Nunca fallback SP (3550308) se UF ≠ SP.
- Frete tem dois campos: **modalidade** `transporte.modFrete` (alias `modalidadeFrete`): 0=emitente, 1=destinatário, 2=terceiros, 3=próprio remetente, 4=próprio destinatário, 9=sem frete (padrão). **Valor** `valorFrete` na raiz (aliases: vFrete, frete, transporte.valorFrete, transporte.valor) — rateado nos itens se eles não tiverem `itens[].valorFrete`. Pagamentos = produtos − descontos + frete.
- Sem ocorrência de transporte: `modFrete = 9` e sem `valorFrete`. Com frete > 0 e modalidade 9, o NFER assume `modFrete = 0`.
- `indPres` (presença do comprador): **default 9** (não presencial, outros). 0=N/A, 1=balcão, 2=internet, 3=teleatendimento, 4=NFC-e entrega, 9=não presencial outros. Balcão → envie `1`; e-commerce explícito → `2`.
- `transporte.volumes[]` (opcional): volumes físicos (`quantidade`, `especie`, `pesoLiquido`/`pesoBruto`). SEFAZ não exige para a maioria das NF-e. Alternativa: peso no produto + `productId` com `modFrete ≠ 9`.
- **IBPT (Lei 12.741):** automático quando `indFinal=1` — `vTotTrib` + texto detalhado no `infCpl`. Base item = produto − desconto + frete. Tabela local (Postgres); sync `POST /v1/ibpt/sync` ou `npm run ibpt:sync`. Não enviar no payload.
- **`informacoesAdicionais`:** texto livre do ERP → `infCpl` → INFORMAÇÕES COMPLEMENTARES. Rodapé: (1) IBPT+ME/EPP na mesma frase; (2) seu texto. XML com ` | ` (sem `\n`); DANFE em dois parágrafos. `informacoesAdicionaisFisco` → RESERVADO AO FISCO.
- `ncm`: 8 dígitos vigentes TIPI.
- Telefone: só dígitos 6–14; vazio → omitir.
- IE cliente: sem IE → indIEDest=9; "ISENTO" → 2.
- CSRT (PR): configurado no **backend NFER**, não no payload do ERP.

## Status NF-e

| status | Significado |
|--------|-------------|
| rascunho | Criada, não enviada |
| processando | Na fila / worker |
| autorizada | cStat 100 |
| denegada | Denegada (cStat 110/301/302) |
| erro | Rejeição — pode /send de novo; ler erro_cstat / erro_motivo |
| cancelada | Cancelada |
| contingencia | NFC-e offline / NF-e EPEC — imprimível; probe retransmite |
| inutilizada | Faixa inutilizada |

## Editar e retransmitir nota com erro

Não existe `/retransmitir`. Fluxo: `PUT /v1/nfe/:id` (mesmo schema de `POST /v1/nfe`) → `POST /v1/nfe/:id/send` no **mesmo id**. Reusa `numero`/`serie` — não cria outra nota.

- PUT só em `rascunho` ou `erro`, modelo 55 (NFC-e não tem este PUT). `autorizada`, `denegada`, `processando` etc. → 400. Substituição completa — não é PATCH de um campo.
- Depois do PUT o status volta a `rascunho` e `erro_cstat`/`erro_motivo` são limpos.
- GET devolve snake_case; não jogue o GET no PUT. Guarde o payload camelCase do POST no ERP e altere o NCM/CFOP.
- `POST /nfe/:id/duplicate` é outro fluxo (próximo número). cStat 656 → esperar ~1h, consultar chave, um send só. `denegada` → não reenviar igual. Painel: editar rascunho/erro + botão Retransmitir.

## Rejeições SEFAZ (cStat) — atalho do integrador

Onde ler: `GET /v1/nfe/:id` → `erro_cstat`/`erro_motivo`; webhook `nfe.erro` → `erroCstat`/`erroMotivo`. Códigos oficiais SEFAZ. A coluna Campo é o JSON do POST /nfe.

| cStat | Campo | Ação |
|-------|-------|------|
| 100 | — | Autorizada; usar chaveAcesso |
| 108/109/584 | — | NFER tenta SVC (55); senão /send |
| 203 | empresa/A1 | Credenciar software na SEFAZ da UF |
| 204/301/302/110 | CNPJ/IE | status denegada; não reenviar igual |
| 206/539 | numero/serie | Consultar existente ou ajustar sequência |
| 209 | empresa.ie | Corrigir IE no painel |
| 213 | A1 | Certificado do mesmo CNPJ-base |
| 210/232 | customer.ie | IE válida, omitir ou ISENTO |
| 225 | itens[].descricao, endereco, cMun | Schema; muitos casos HTTP 400 antes |
| 228 | — | Não atrasar o /send |
| 252 | empresa.environment | Homologação vs produção |
| 274/275 | customer.endereco.cMun | UF+xMun ou CEP; NFER resolve |
| 778 | itens[].ncm | TIPI vigente; PUT + /send |
| 590/591 | impostos / fiscalProfileId | CRT=1 → CSOSN; CRT=3 → CST |
| 610 | itens + valorFrete + pagamentos | Total = produtos − desc + frete |
| 611 | itens[].ean | Omitir ou GTIN válido |
| 732/733 | itens[].cfop / perfil | 5xxx interna; 6xxx interestadual |
| 806 | CEST | ST exige CEST CONFAZ do NCM |
| 865 | pagamentos[].valor | Soma = vNF (HTTP 400 na API) |
| 696 | indFinal + customer.ie | Dest sem IE exige indFinal=1; NFER normaliza |
| 321/1048/1102 | itens[].nfeReferenciada | chaveAcesso + nItem do XML original |
| 1010 | nfeReferenciada | Só ref. por item na devolução |
| 1072 | nfeReferenciada | Não repetir o mesmo par |
| 1020–1024 / 1104–1119 | IBS/CBS | CST/cClassTrib conforme NT |
| 391 | pagamentos[].forma | Forma coerente (cartão) |
| 434/435 | indPres | NFER envia indIntermed=0 no XML |
| 656 | — | ~1h; não martelar /send; consultar chave |
| 694 | cBenef | Quando CST/UF exigir |
| 703 | — | Relógio; NFER usa Brasília |
| 713 | — | Fallback SVC ajusta tpEmis 6/7 |
| 724 | customer.nome | Informar nome |
| 978 | RESP_TEC (NFER) | CSRT no backend; comum no PR |

Catálogo completo: Portal Nacional NF-e. Docs: /docs/nfe/erros. Blog por código = backlog.

## Erros HTTP (resumo)

- 400 VALIDATION_ERROR — corrigir payload
- 401 — key/JWT inválido
- 403 — empresa inativa / sem permissão
- 404 — não encontrado
- 429 TOO_MANY_REQUESTS — ler `Retry-After`, aguardar e tentar de novo
- 502 SEFAZ_ERROR — ver motivo; corrigir dados fiscais
- 500 — backoff

## Autenticação — exemplos

```http
X-API-Key: a0eebc99.seu-segredo
```

Rotas públicas: `GET /health`, `POST /v1/companies`.

## Quick Start — payloads

### Cliente

`POST /v1/customers` é opcional. `cMun` no endereço é opcional se houver UF+cidade ou CEP.

```json
{
  "nome": "Cliente Exemplo LTDA",
  "cpfCnpj": "00000000000191",
  "tipoPessoa": "J",
  "endereco": {
    "logradouro": "Rua Exemplo",
    "numero": "100",
    "bairro": "Centro",
    "xMun": "Curitiba",
    "UF": "PR",
    "CEP": "80010000"
  }
}
```

### Criar NF-e — cliente no JSON

`POST /v1/nfe`. Destinatário no body. Sem IE = consumidor final. Pagamento (PIX 17) = produtos + frete. `fiscalProfileId` no item: o NFER preenche CFOP e impostos.

```json
{
  "customer": {
    "cpfCnpj": "12345678000199",
    "nome": "Cliente Exemplo LTDA",
    "email": "fiscal@cliente.com",
    "telefone": "4430000000",
    "endereco": {
      "logradouro": "Rua das Flores",
      "numero": "100",
      "complemento": "Sala 2",
      "bairro": "Centro",
      "xMun": "Cianorte",
      "UF": "PR",
      "CEP": "87200000"
    }
  },
  "naturezaOperacao": "VENDA DE MERCADORIA",
  "tipoOperacao": "1",
  "finalidade": 1,
  "serie": 1,
  "indFinal": 1,
  "indPres": 2,
  "valorFrete": 15.94,
  "transporte": { "modFrete": 0 },
  "informacoesAdicionais": "Pedido 3306",
  "itens": [{
    "codigo": "SKU-001",
    "descricao": "Camiseta algodão",
    "ncm": "61091000",
    "unidade": "UN",
    "quantidade": 1,
    "valorUnitario": 259.90,
    "fiscalProfileId": "UUID-DO-PERFIL"
  }],
  "pagamentos": [{ "forma": "17", "valor": 275.84 }]
}
```

### Criar NF-e — cliente já cadastrado

Mesma nota. Troca `customer` por `customerId` (UUID de `POST /v1/customers`).

```json
{
  "customerId": "UUID-DO-CLIENTE",
  "naturezaOperacao": "VENDA DE MERCADORIA",
  "tipoOperacao": "1",
  "finalidade": 1,
  "serie": 1,
  "indFinal": 1,
  "indPres": 2,
  "valorFrete": 15.94,
  "transporte": { "modFrete": 0 },
  "informacoesAdicionais": "Pedido 3306",
  "itens": [{
    "codigo": "SKU-001",
    "descricao": "Camiseta algodão",
    "ncm": "61091000",
    "unidade": "UN",
    "quantidade": 1,
    "valorUnitario": 259.90,
    "fiscalProfileId": "UUID-DO-PERFIL"
  }],
  "pagamentos": [{ "forma": "17", "valor": 275.84 }]
}
```

### Criar NF-e — sem perfil (impostos no JSON)

Quando o ERP já tem a tributação, omita `fiscalProfileId` e envie `impostos` + `cfop` + `ncm`. CFOP é obrigatório se não vier de produto/perfil.

Campos de `impostos`:
- `icms`: `cst` **ou** `csosn`, `aliquota?`, `base?`, `origem?`
- `pis`: `cst` (obrig.), `aliquota?`
- `cofins`: `cst` (obrig.), `aliquota?`

### Devolução (`finalidade: 4`)

Em cada item envie `nfeReferenciada: { chaveAcesso, nItem }` (44 dígitos + nItem do XML original). Vira `DFeReferenciado` no XML. Obrigatório salvo CFOPs 1201/1202/1410/1411/5921/6921. Evita rejeição 321/1102 (regra forte a partir de 01/09/2026).

```json
{
  "finalidade": 4,
  "tipoOperacao": "0",
  "itens": [{
    "descricao": "ITEM DEVOLVIDO",
    "ncm": "87089990",
    "cfop": "1202",
    "unidade": "UN",
    "quantidade": 1,
    "valorUnitario": 100,
    "nfeReferenciada": {
      "chaveAcesso": "35260736848840000156550090000000111223152566",
      "nItem": 7
    }
  }]
}
```

CRT 1 → CSOSN; CRT 3 → CST.

```json
{
  "customerId": "UUID-DO-CLIENTE",
  "naturezaOperacao": "VENDA DE MERCADORIA",
  "tipoOperacao": "1",
  "finalidade": 1,
  "serie": 1,
  "indFinal": 0,
  "indPres": 9,
  "transporte": { "modFrete": 9 },
  "itens": [{
    "codigo": "PROD001",
    "descricao": "Produto X",
    "ncm": "61091000",
    "cfop": "5102",
    "unidade": "UN",
    "quantidade": 2,
    "valorUnitario": 150.0,
    "origem": 0,
    "impostos": {
      "icms": { "csosn": "102", "aliquota": 0, "origem": 0 },
      "pis": { "cst": "07", "aliquota": 0 },
      "cofins": { "cst": "07", "aliquota": 0 }
    }
  }],
  "pagamentos": [{ "forma": "01", "valor": 300.0 }]
}
```

Exemplo Regime Normal (CST + alíquotas):

```json
{
  "codigo": "SKU-00",
  "descricao": "PECA INDUSTRIAL",
  "ncm": "84879000",
  "cfop": "6102",
  "unidade": "UN",
  "quantidade": 1,
  "valorUnitario": 1200.0,
  "origem": 0,
  "impostos": {
    "icms": { "cst": "00", "aliquota": 12, "origem": 0 },
    "pis": { "cst": "01", "aliquota": 1.65 },
    "cofins": { "cst": "01", "aliquota": 7.6 }
  }
}
```

Híbrido: `productId` (catálogo) + `impostos` ou `fiscalProfileId` no mesmo item. Se perfil **e** impostos, o body preenche lacunas do perfil.

### Editar (rascunho / erro)

```http
PUT https://api.nfer.me/v1/nfe/{id}
X-API-Key: $NFER_KEY
Content-Type: application/json

{ /* mesmo body de POST /nfe, com NCM/CFOP/etc. corrigidos */ }
```

→ 200, status `rascunho`, mesmo `numero`/`serie`. Depois: `POST /nfe/{id}/send`.

### Send

```http
POST https://api.nfer.me/v1/nfe/{id}/send
X-API-Key: $NFER_KEY
```

→ 202 `{ "message": "NF-e queued for sending", "jobId": "...", "status": "processando" }`

### Lote (vários pedidos)

```http
POST https://api.nfer.me/v1/nfe/batch
X-API-Key: $NFER_KEY
Content-Type: application/json

{ "modelo": "55", "send": true, "items": [ /* mesmo body de POST /nfe */ ] }
```

→ 202 `{ batchId, queued, status, items }`. Máx. 50. Status: `GET https://api.nfer.me/v1/nfe/batch/{batchId}`. Webhook `nfe.batch.completed`.

### Cancelar / inutilizar

Inutilização é **síncrona** com a SEFAZ; sucesso = cStat `102`.

```json
POST /v1/nfe/:id/cancel
{ "justificativa": "erro de digitação no item" }
```

```json
POST /v1/nfe/inutilizar
{
  "serie": 1,
  "numeroInicial": 105,
  "numeroFinal": 110,
  "justificativa": "Falha na sequência numérica do ERP"
}
```

Justificativa mín. 15 caracteres. Campo opcional `modelo`: `"55"` (default) ou `"65"` (NFC-e).

### NFC-e (modelo 65)

Operacional via `/v1/nfce` e painel `/dashboard/nfe/create-nfce`. Destinatário (`customerId`) opcional.

1. Configure CSC: `PUT /v1/companies/:id` com `nfceIdCsc` + `nfceTokenCsc`
2. `POST /v1/nfce` → rascunho modelo 65
3. `POST /v1/nfce/:id/send` → 202 (mesma fila; ramo NFC-e)
4. `GET /v1/nfce/:id` / `GET /v1/nfce/:id/xml` / `GET /v1/nfe/:id/danfe` (cupom; também em `erro` ou `contingencia`)

Contingência: SEFAZ fora → status `contingencia` + webhook `nfe.contingencia` (sempre `tpEmis=9` offline). Retransmissão automática quando o serviço volta (probe ~15 min). EPEC (`tpEmis=4`) é opcional por UF — não é o padrão NFER.
Cancelamento: mesmo `POST /v1/nfe/:id/cancel` (worker detecta modelo 65).

### Devolução a partir de NF-e autorizada

`POST /v1/nfe/:id/devolucao` cria rascunho (`finalidade: 4`, `tipoOperacao: "0"`) com `nfeReferenciada` (chave + nItem) em cada item e CFOP de entrada espelhado (5xxx→1xxx, 6xxx→2xxx).

No painel: menu da nota autorizada → **Emitir devolução** (abre o formulário pré-preenchido).

Filtros da listagem:
- `tipo` = direção tpNF (`0` entrada / `1` saída)
- `finalidade` = finNFe (`1` normal, `2` complementar, `3` ajuste, `4` devolução)

### Logs HTTP (api_logs)

`GET /v1/companies/:id/logs?limit=50&group=nfe`

Só entram chamadas com `X-API-Key` (ERP) e testes de webhook feitos no painel (`POST /webhooks/...`).

`group`: `todos` | `nfe` | `customers` | `products` | `carriers` | `fiscal` | `companies` | `webhooks`.
Auditoria SEFAZ (payload XML/JSON) fica na tabela `logs` com `tipo` (`nfe:send`, `nfe:inutilizar`) — canal separado do HTTP.

## Empresa

- `POST /v1/companies` (público): cnpj, razaoSocial, crt (1|2|3), endereco, environment?, partner?, partnerRef?, `planId` + `couponCode` no cadastro direto. Preview: `GET /v1/coupons/preview?code=&planId=`.
- `PUT /v1/companies/:id`: environment homologacao|producao; email; `nfceIdCsc` / `nfceTokenCsc` (CSC NFC-e); etc.
- `POST /v1/companies/:id/certificate`: A1
- `POST /v1/companies/:id/logo`: logomarca DANFE (parceiros). **Não** usar /avatar da Linka.
- Séries: `GET/POST /v1/companies/:id/nfe-sequence(s)` — uma série ativa por modelo×ambiente; lastNumero = último usado.
- E-mail: `GET/PUT /v1/companies/:id/email-settings` (SMTP próprio; credenciais da NFER nunca voltam). Preferências + e-mail fiscal: `PUT .../email-settings/delivery`. ZIP do mês anterior agora: `POST .../email-settings/xml-monthly`.

## E-mail fiscal (produto)

Painel → Configurações → E-mail. `emails_enabled` desliga tudo. Tipos: convite, XML+DANFE por NF-e autorizada, lote mensal no Growth+ (dia 1–28, competência do mês anterior: `{cnpj}/{yyyy-mm}/xml|danfe`). SMTP automático (env `SMTP_*`) ou custom por empresa.

## CRT / impostos

- CRT 1 Simples → CSOSN (102, 500…)
- CRT 2/3 → CST
- Perfis: `/v1/fiscal-profiles` + `fiscalProfileId` no item
- Override: objeto `impostos` no item (ignora perfil; exige ncm/cfop/unidade)

## Outros recursos

- Produtos: `/v1/products` — peso opcional (kg/unid.): `peso` (único; replica líquido e bruto) ou `pesoLiquido` + `pesoBruto`. Na NF-e com `modFrete ≠ 9`, soma qtd × peso nos volumes.
- Transportadoras: `/v1/carriers`
- Lista NF-e: `GET /v1/nfe?page&limit&status&tipo&finalidade&from&to` (`from`/`to` = YYYY-MM-DD, para export diário/mensal)
- Status+SEFAZ (nota): `GET /v1/nfe/:id/status`
- Status autorizador SEFAZ (UF): `GET /v1/sefaz/status` — cStat 107 = online; inclui `contingencyMode` NFER; `?force=1` ignora cache

## Ambiente

`environment` da empresa: `homologacao` (tpAmb=2) ou `producao` (tpAmb=1). Contadores de série **independentes** por ambiente.

## Planos NFER (clientes diretos)

Trial: 30 dias. Parceiros usam `plan_tier` externo.

| Plano | NF-e/mês | CNPJs | Preço | Extra | XML mensal |
|-------|----------|-------|-------|-------|------------|
| Start | 100 | 2 CNPJs | R$ 49,90 | R$ 0,50 | não |
| Growth (Mais usado) | 500 | 10 CNPJs | R$ 99,90 | R$ 0,22 | sim |
| Business | 2.200 | CNPJs ilimitados | R$ 199,90 | R$ 0,17 | sim |
| Enterprise | 12.000 | CNPJs ilimitados | R$ 399,90 | R$ 0,10 | sim |

## Instruções para o agente

1. Sempre use `X-API-Key` server-side; nunca no frontend público.
2. Fluxo create → send → webhook (ou poll); use HMAC nos webhooks.
3. Erros: HTTP usa `error` (VALIDATION_ERROR, TOO_MANY_REQUESTS, CERTIFICATE_ERROR…); rejeição SEFAZ usa `erro_cstat` / `erro_motivo` no GET (webhook: `erroCstat` / `erroMotivo` em `nfe.erro`). Em `status=erro`, corrija e `/send` de novo (mesmo número). `denegada` (110/301/302) = não reenviar igual.
4. Valide destinatário (customerId ou customer), município (cMun opcional se UF+cidade/CEP) e NCM antes do POST. Frete: valorFrete separado de modFrete/modalidadeFrete; pagamentos incluem o frete. `indPres`/`indFinal` e `itens[].valorDesconto` existem na API e no painel Create NF-e (ex.: venda internet = indPres 2). Destinatário sem IE → SEFAZ exige `indFinal=1` (cStat 696); o NFER normaliza se vier 0.
5. Prefira `fiscalProfileId` (smart) quando o ERP não calcula imposto; use `impostos`+`cfop` (full) quando o ERP já tem a tributação.
6. cStat frequentes: 225 schema; 539/206 duplicidade; 321/1102/1010 devolução (nfeReferenciada por item); 978 CSRT (PR); 108/109/584 → SVC automático; 1020–1024 IBS/CBS. Ver /docs/nfe/erros.
7. DANFE atual: A4 normal (NF-e tpImp=1) e cupom NFC-e (tpImp=4). Não há DANFE Simplificado (tpImp 3/6) ainda.
8. Para parceiros Linka: login `https://nfer.me/login?tab=apikey&companyId=<uuid>`.
