Portal do Desenvolvedor FOX NF-e

Integração fiscal por API REST, com SDKs oficiais e webhooks assinados. Esta documentação descreve o que já está em operação e sinaliza, com honestidade, o que ainda está em revisão.

Comece por aqui: a API vive em https://www.foxnfe.com.br/api/v1. Toda requisição autenticada leva dois cabeçalhos — Authorization: Bearer <token> e X-Tenant-ID: <slug-do-tenant>. O token é emitido pela API (POST /auth/login) e deve ficar guardado no servidor do integrador (variável de ambiente ou cofre de segredos) — nunca no código do navegador nem em log. Faça primeiro chamadas de leitura, como GET /auth/me, antes de qualquer emissão.

URL base e verificação de saúde #

A URL base canônica é https://www.foxnfe.com.br/api/v1. Os endpoints protegidos respondem 401 sem credenciais válidas — isso verifica a conectividade do endpoint e a exigência de autenticação, sem comprovar os fluxos autenticados. Use uma chamada de leitura para validar conectividade e TLS antes de escrever qualquer fluxo de emissão.

bash — verificação read-only
# Sem token: deve responder 401 (autenticação exigida)
curl -i https://www.foxnfe.com.br/api/v1/auth/me

# Com token de servidor (nunca embutido no front-end)
curl -s https://www.foxnfe.com.br/api/v1/auth/me \
  -H "Authorization: Bearer $FOXNFE_TOKEN" \
  -H "X-Tenant-ID: $FOXNFE_TENANT"
Tenant é o slug, não um número. O cabeçalho X-Tenant-ID recebe o identificador textual do tenant (por exemplo minha-empresa), não um UUID nem o ID inteiro interno.

Primeiros passos, na ordem certa #

Uma primeira integração segura lê antes de escrever. Comece confirmando identidade e configuração; só depois avance para emissão em ambiente de homologação.

  1. Confirme o token com GET /auth/me — retorna o usuário e o tenant vinculados ao token.
  2. Inspecione o webhook com GET /webhooks/config — mostra se há URL configurada, sem revelar o segredo.
  3. Escolha o ambiente pelo campo ambiente do payload (2 = homologação) — veja Autenticação e ambientes.
  4. Trate o 202 como "enfileirado", não como "autorizado pela SEFAZ" — veja Emissão assíncrona.
Nunca registre o token. Não use console.log, print ou echo com o token ou o segredo de webhook. Eles pertencem a variáveis de ambiente do servidor e a um cofre de segredos — jamais ao histórico de terminal, a logs ou ao bundle do navegador.

Autenticação e ambientes #

Autentique com dois cabeçalhos e escolha o ambiente pelo payload. Todo endpoint protegido exige, em conjunto, Authorization: Bearer <token> e X-Tenant-ID: <slug>. Para NF-e/NFC-e, produção e homologação são selecionadas pelo campo ambiente do corpo da requisição (1 = produção, 2 = homologação) — trocar o hostname não muda o ambiente fiscal. Essa é a convenção da NF-e/NFC-e; a NFSe (Padrão Nacional) tem contrato próprio e ainda está em revisão, então não presuma o mesmo campo para todos os documentos.

Cabeçalhos obrigatórios

CabeçalhoValorObservação
AuthorizationBearer <token>Token emitido pelo servidor; carregado de variável de ambiente.
X-Tenant-ID<slug>Slug textual do tenant (ex.: minha-empresa).
Content-Typeapplication/jsonEm requisições com corpo.
Acceptapplication/jsonRespostas de erro seguem o padrão Laravel (message + errors).

Obter e reutilizar o token

Um token pode ser obtido por POST /auth/login (e-mail e senha) ou injetado a partir de um segredo já persistido. Em serviços de longa duração, prefira injetar um token guardado no cofre a autenticar a cada chamada.

Nunca passe a senha na linha de comando. Credenciais em argumentos ficam visíveis em ps e no histórico do shell, e interpolar a senha em JSON de curl quebra com caracteres especiais. Leia e-mail e senha de variáveis de ambiente, monte o corpo com um serializador JSON e grave o token direto no cofre — sem ecoá-lo.
python — obtém o token e cria um arquivo privado (Linux/macOS)
#!/usr/bin/env python3
# Somente biblioteca padrão. Credenciais vêm do ambiente; o token é gravado
# num arquivo 0600 (ou entregue ao cofre) e NUNCA impresso no stdout.
import json, os, urllib.request

body = json.dumps({                       # json.dumps escapa o conteúdo corretamente
    "email":    os.environ["FOXNFE_EMAIL"],
    "password": os.environ["FOXNFE_PASSWORD"],
}).encode("utf-8")

req = urllib.request.Request(
    "https://www.foxnfe.com.br/api/v1/auth/login",
    data=body, method="POST",
    headers={
        "Content-Type": "application/json",
        "Accept":       "application/json",
        "X-Tenant-ID":  os.environ["FOXNFE_TENANT"],
    },
)
with urllib.request.urlopen(req, timeout=30) as resp:
    token = json.load(resp)["token"]      # não faça print(token)

# Use um caminho novo em um diretório privado, pertencente ao usuário do serviço.
# O_EXCL recusa arquivos existentes e links simbólicos; nunca sobrescreve um token.
# No Windows, use um cofre ou controle de acesso equivalente em vez deste exemplo POSIX.
fd = os.open(os.environ["FOXNFE_TOKEN_FILE"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "w") as f:
    f.write(token)
print("token obtido e gravado com permissão 600")   # confirma sem revelar o segredo

Produção × homologação (NF-e/NFC-e)

Na NF-e/NFC-e, o ambiente fiscal é decidido pelo campo ambiente dentro do payload de emissão: 1 para produção e 2 para homologação. Esse valor viaja no corpo da requisição, não na URL. Um mesmo token e uma mesma URL base atendem os dois ambientes — a diferença está no que você declara no documento. Para a NFSe (Padrão Nacional), o contrato de ambiente é próprio e está em revisão; confira o estado em Estado dos domínios fiscais antes de assumir a mesma convenção.

Homologação não tem valor fiscal. Documentos emitidos com ambiente: 2 são de teste na SEFAZ e não substituem emissão de produção.

Emissão assíncrona: o que o 202 significa

A emissão é assíncrona. Ao aceitar uma emissão, a API responde imediatamente com o recurso em estado pending (nas respostas HTTP diretas isso corresponde a 202 Accepted). Isso quer dizer enfileirado para processamento — e não autorizado pela SEFAZ.

202 ≠ autorizado. Só o estado authorized, acompanhado do protocolo fiscal correspondente, confirma a autorização. Sair de pending não é, por si só, autorização: rejected é recusa pela SEFAZ, e estados intermediários como processing ou de erro (failed) não confirmam nada. Consulte o status por polling ou aguarde o webhook nfe.authorized, e nunca trate o 202 como confirmação de autorização fiscal.

Idempotência, erros e novas tentativas

A idempotência de emissão está vinculada ao par payload/tenant: reenviar o mesmo documento para o mesmo tenant não deve gerar duplicidade silenciosa. Diante de um status ambíguo (timeout, 5xx, conexão perdida), não reemita às cegas — primeiro consulte o status do recurso e só então decida. Erros de validação chegam como 422 com o mapa errors; erros de autenticação, como 401/403.

SDKs oficiais #

Há SDKs para Node.js (≥18), Python (≥3.9) e PHP (≥8.1), todos na versão 1.3.0. Os pacotes são distribuídos por download direto (não estão publicados em npm, PyPI ou Packagist). Em todos eles, informe a baseUrl canônica explicitamente e carregue token e tenant de variáveis de ambiente.

Distribuição por download, não por registry. A publicação nos registries públicos ainda não foi confirmada. Use os arquivos empacotados a partir da origem oficial de download: https://docs.foxnfe.com.br/downloads/…. Comandos como npm install foxnfe puro (nome no registry) não são garantidos.
baseUrl explícita. O valor embutido por padrão em algumas versões do SDK aponta para um host legado. Sempre passe baseUrl: "https://www.foxnfe.com.br/api/v1" (Node), base_url=… (Python) ou o segundo argumento do construtor (PHP) para fixar a URL canônica.

Node.js (≥ 18)

Instale a partir do tarball oficial e crie o cliente com a URL base explícita. As classes de módulo (Nfe, Nfse, Webhook, Reference, Documents, NfeEvents, Distribuicao, Rtc, Support) recebem o cliente no construtor.

bash — instalação por tarball
# A partir do download oficial (recomendado)
npm install https://docs.foxnfe.com.br/downloads/foxnfe-1.3.0.tgz

# Ou a partir do arquivo local entregue à sua equipe
npm install ./foxnfe-1.3.0.tgz
consumidor.mjs — leitura, sem emissão
import { createClient, Webhook } from 'foxnfe';

const client = createClient({
  tenantSlug: process.env.FOXNFE_TENANT,      // slug, não UUID
  token:      process.env.FOXNFE_TOKEN,       // token do cofre/env
  baseUrl:    'https://www.foxnfe.com.br/api/v1',
});

// Chamadas de leitura para validar a integração:
const me = await client.me();
// A resposta é aninhada: { user: { …, tenant_id }, tenant: { id, slug, name, status }, subscription, abilities }
console.log('tenant (slug):', me.tenant?.slug ?? '(sem tenant)');
console.log('tenant_id:', me.user?.tenant_id ?? '(sem tenant)');

const webhook = new Webhook(client);
const cfg = await webhook.getConfig();
console.log('webhook configurado?', cfg.configured);

Python (≥ 3.9)

Instale a wheel oficial e instancie Client com base_url. Os módulos são expostos como propriedades (client.nfe, client.nfse, client.webhook, client.reference, client.documents, client.nfe_events, client.distribuicao, client.rtc, client.support).

bash — instalação por wheel
pip install https://docs.foxnfe.com.br/downloads/foxnfe-1.3.0-py3-none-any.whl
# Ou local:
pip install ./foxnfe-1.3.0-py3-none-any.whl
consumidor.py — leitura, sem emissão
import os
from foxnfe import Client

client = Client(
    tenant_slug=os.environ["FOXNFE_TENANT"],   # slug, não UUID
    token=os.environ["FOXNFE_TOKEN"],          # token do cofre/env
    base_url="https://www.foxnfe.com.br/api/v1",
)

me = client.me()
# Resposta aninhada: {"user": {..., "tenant_id"}, "tenant": {"id", "slug", ...}, ...}
print("tenant (slug):", (me.get("tenant") or {}).get("slug", "(sem tenant)"))
print("tenant_id:", (me.get("user") or {}).get("tenant_id", "(sem tenant)"))

cfg = client.webhook.get_config()
print("webhook configurado?", cfg.get("configured"))

PHP (≥ 8.1)

O pacote depende de Guzzle. Aponte um repositório Composer do tipo package para o zip oficial e então instale por composer require.

composer.json — repositório do tipo package
{
  "repositories": [
    {
      "type": "package",
      "package": {
        "name": "foxdigital/foxnfe-php",
        "version": "1.3.0",
        "dist": {
          "type": "zip",
          "url": "https://docs.foxnfe.com.br/downloads/foxnfe-php-1.3.0.zip"
        },
        "require": { "php": ">=8.1", "guzzlehttp/guzzle": "^7.0" },
        "autoload": { "psr-4": { "FoxNfe\\": "src/" } }
      }
    }
  ]
}
bash
composer require foxdigital/foxnfe-php:1.3.0
consumidor.php — leitura, sem emissão
<?php
require 'vendor/autoload.php';

use FoxNfe\Client;

$client = (new Client(
    tenantSlug: getenv('FOXNFE_TENANT'),                 // slug, não UUID
    baseUrl:    'https://www.foxnfe.com.br/api/v1',
))->withToken(getenv('FOXNFE_TOKEN'));                   // token do cofre/env

$me = $client->me();
// Resposta aninhada: ['user' => [..., 'tenant_id'], 'tenant' => ['id','slug',...], ...]
echo 'tenant (slug): ' . ($me['tenant']['slug'] ?? '(sem tenant)') . PHP_EOL;
echo 'tenant_id: '     . ($me['user']['tenant_id'] ?? '(sem tenant)') . PHP_EOL;

$cfg = $client->webhook()->getConfig();
echo 'webhook configurado? ' . var_export($cfg['configured'] ?? false, true) . PHP_EOL;
Exemplos executáveis offline. Cada exemplo abaixo importa as classes reais do SDK foxnfe 1.3.0 e substitui apenas o transporte HTTP por um mock — nenhuma chamada de rede, SMTP ou emissão. Eles exercitam o cliente (me(), webhook.getConfig()) e a verificação de assinatura HMAC sobre o corpo canônico, servindo de base segura para os seus próprios scripts de leitura.

Os contratos dos módulos novos (referência, documentos, CC-e, recebidos, catálogo de webhooks) têm demos offline distribuídos junto de cada pacote: examples/reference_documents_offline_demo.{mjs,py,php}.

Webhooks #

Os eventos chegam como CloudEvents v1.0 assinados por HMAC-SHA256. Cada entrega traz o cabeçalho X-Webhook-Signature: sha256=<hex>, calculado sobre os bytes exatos do corpo. Verifique a assinatura sobre o corpo cru recebido, com comparação em tempo constante, e use o id do CloudEvent para deduplicar. Este é o domínio já revisado e pronto para integração.

Configurar o webhook

A configuração é por tenant. O segredo deve ser definido por você: gere um valor aleatório de alta entropia, envie-o em webhook_secret e guarde a mesma cópia na sua integração — é ela que valida a assinatura. Ao omitir webhook_secret numa atualização, o valor atual é preservado; o DELETE apenas desativa o webhook (active: false) e preserva o segredo para reativação por um novo PUT. A API nunca devolve o segredo: tanto GET quanto PUT retornam apenas secret_preview mascarado.

OperaçãoMétodo / rotaNotas
ConsultarGET /webhooks/configRetorna configured e, se houver, webhook_url + secret_preview + active. Não retorna o segredo.
Criar/atualizarPUT /webhooks/configCorpo { webhook_url, webhook_secret? }. URL deve ser HTTPS pública; hosts privados/loopback são rejeitados (anti-SSRF) com 422.
DesativarDELETE /webhooks/configPreserva o histórico; não há exclusão silenciosa das entregas.
Gere e envie o seu próprio segredo. Forneça em webhook_secret um valor aleatório de pelo menos 16 caracteres, conhecido só pela sua integração — é o que permite validar o HMAC. Se você não enviar um segredo, o servidor gera um automaticamente no primeiro cadastro, mas esse valor nunca é retornado (a resposta traz apenas o preview mascarado): sem conhecê-lo, você não consegue verificar a assinatura. Nesse caso, faça um PUT com um webhook_secret seu antes de começar a verificar. Para rotacionar, basta um novo PUT com o novo valor.

Envelope CloudEvents v1.0

Cada evento é um CloudEvent no formato abaixo. O id é estável e coincide com o delivery_id — use-o para deduplicação idempotente no consumidor. O catálogo vigente, com os campos garantidos em data, está em GET /webhooks/events (também nos SDKs: webhook.events()).

exemplo de envelope
{
  "specversion":     "1.0",
  "id":              "550e8400-e29b-41d4-a716-446655440000",
  "source":          "https://foxnfe.com.br",
  "type":            "br.com.centralfox.foxnfe.nfe.authorized",
  "datacontenttype": "application/json",
  "time":            "2026-09-05T12:00:00-03:00",
  "data":            { "nfe_id": 123, "status": "authorized" }
}
Tipo (após o prefixo br.com.centralfox.foxnfe.)Quando
nfe.authorizedNF-e autorizada pela SEFAZ
nfe.rejectedNF-e rejeitada pela SEFAZ
nfe.cancelledNF-e cancelada
nfse.authorizedNFSe autorizada
nfse.rejectedNFSe rejeitada
nfe.testEvento sintético de teste (não emite documento)
tenant.registeredTenant cadastrado
onboarding.completedOnboarding concluído
certificate.validatedCertificado A1 validado
nfe.contingencyNF-e estacionada em contingência durável (não transmitida)
nfe.cce.registered / nfe.cce.rejectedCarta de Correção registrada (cStat 135) ou rejeitada de forma definitiva
nfe.receivedNF-e destinada ao seu CNPJ recebida pela distribuição (resumo ou XML integral)
nfe.manifestacao.transmitted / nfe.manifestacao.rejectedManifestação do destinatário registrada ou rejeitada pela SEFAZ
document.importedXML de terceiros importado para a Central de Documentos
nfse.cancelled / nfse.substitutedNFS-e Nacional cancelada ou substituída
certificate.expiring / certificate.expiredCertificado A1 ativo perto do vencimento ou expirado
nfe.ator.registered / nfe.ator.rejectedAtor interessado (110150) registrado ou rejeitado de forma definitiva
nfe.insucesso.registered / nfe.insucesso.rejectedInsucesso na entrega (110192) ou seu cancelamento (110193) registrado ou rejeitado
nfe.inutilizacao.registered / nfe.inutilizacao.rejectedInutilização de faixa de numeração homologada (cStat 102/206) ou rejeitada
nfe.econf.registered / nfe.econf.rejectedConciliação financeira (110750) ou seu cancelamento (110751) registrado ou rejeitado
nfe.rtc.registered / nfe.rtc.rejectedEvento RTC de IBS/CBS (NT 2025.002) ou cancelamento de evento (110001) registrado ou rejeitado — nota própria ou recebida

Verificar a assinatura (HMAC-SHA256)

Verifique sobre o corpo cru, sem reserializar. A assinatura é o HMAC-SHA256 dos bytes exatos enviados (JSON canônico, com chaves ordenadas). Se você reparsear e reserializar o JSON antes de verificar, a ordem das chaves pode mudar e a assinatura deixa de bater. Compare em tempo constante.

node — express
import crypto from 'node:crypto';
import express from 'express';

const app = express();
// IMPORTANTE: capture o corpo CRU; não use express.json() antes de verificar.
app.use('/hooks/foxnfe', express.raw({ type: '*/*' }));

app.post('/hooks/foxnfe', (req, res) => {
  const secret = process.env.FOXNFE_WEBHOOK_SECRET;
  const header = req.get('X-Webhook-Signature') || '';
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(req.body)               // Buffer cru, exatamente como recebido
    .digest('hex');

  const a = Buffer.from(header);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).end();   // assinatura inválida
  }

  const event = JSON.parse(req.body.toString('utf8')); // só depois de verificar
  // deduplicar por event.id antes de processar
  res.status(204).end();
});

Entregas, tentativas e reenvio

Cada entrega é tentada até 3 vezes, com espera de 30 s após a primeira falha e 300 s após a segunda. O histórico paginado (15 por página) fica em GET /webhooks/deliveries. Uma rotina de reconciliação recoloca na fila apenas entregas ainda não tentadas, presas entre o commit e o enfileiramento, com idade mínima de 5 minutos — entregas já falhadas ou em backoff nunca são redirigidas por ela.

Entrega é at-least-once, não exactly-once. Um timeout pode ocorrer depois que seu endpoint já recebeu os bytes. Trate duplicatas deduplicando pelo id do CloudEvent (que é persistente) e responda rápido (2xx) para confirmar o recebimento.

Para reenviar manualmente uma entrega em falha terminal, use POST /webhooks/deliveries/{delivery_id}/resend. A operação é auditada (ator, tenant, data e tentativas anteriores) antes de zerar o contador. Os códigos abaixo vêm do controlador atual:

CódigoSignificado
202Reenfileirada; o job é despachado após o commit para evitar duplo enfileiramento.
404UUID inválido, ou entrega inexistente/pertencente a outro tenant (não distingue, para não vazar existência).
409Entrega já realizada com sucesso.
422Entrega não está em falha terminal (ainda pendente); nada é reenfileirado.
429Limite de 10 requisições por minuto atingido.
503Não foi possível confirmar o enfileiramento; a falha fica recuperável e auditada. Consulte o histórico antes de repetir.

O endpoint POST /webhooks/test enfileira um evento sintético nfe.test para validar URL, assinatura e parsing sem emitir documento real (202; 422 se não houver webhook ativo; 429 no limite de taxa).

Consultas auxiliares #

Catálogos oficiais para preencher cadastros sem digitar código errado. Todas as rotas ficam sob /reference-data, aceitam qualquer token do tenant com ability reference:read (ou fiscal) e devolvem source com origem, versão, hash e datas reais. São consultas de preenchimento — nunca orientação tributária.

RotaConteúdoNotas
GET /reference-data/municipalities[/{ibge}]Municípios IBGE (5.571)Filtros q, uf; código de 7 dígitos.
GET /reference-data/cfop[/{code}]CFOP consolidadoselectable distingue operações de cabeçalhos; effective_at ≠ publicação.
GET /reference-data/cnae[/{code}]CNAE 2.0 subclasses7 dígitos.
GET /reference-data/ncm[/{code}]NCM vigenteSó o código de 8 dígitos é selecionável; os demais são hierarquia.
GET /reference-data/servicos-nacionais[/{cTribNac}]Lista de Serviços Nacional (NFS-e)6 dígitos selecionáveis; cabeçalhos item/subitem vêm com code_synthetic=true. Não converte para código municipal.
GET /reference-data/nbs[/{code}]NBS 2.09 dígitos selecionáveis; 5/6 são posição/subposição.
GET /reference-data/cep/{cep}Endereço por CEP404 inexistente; 503 provedor indisponível (nunca cacheado); municipality_known cruza o IBGE devolvido com o catálogo local.
GET /reference-data/cnpj/{cnpj}Dados cadastrais públicosDígitos verificadores conferidos antes de consultar (422); somente empresa, endereço e atividade principal; atualidade é a do provedor.

Paginação: page e per_page (máximo 100). Entrada inválida responde 422; catálogo não provisionado responde 503 tipado, nunca uma amostra apresentada como catálogo completo.

Central de Documentos #

Emitidas, recebidas e importadas num único índice, com exportação verificável. A importação de XML de terceiros tem prévia sem persistência e commit amarrado ao hash dos mesmos bytes; documento importado nunca vira "autorizado pelo FoxNFe".

OperaçãoMétodo / rotaNotas
ConsultarGET /documentsFiltros de período, origem (emitida, recebida, importada), tipo, status e participante; sort, page, per_page.
DetalheGET /documents/{source}/{id}source é a origem física (nfes, nfses, nfe_distribuicoes, imported_documents).
Prévia de importaçãoPOST /documents/imports/previewmultipart, campo file (XML UTF-8 até 2 MiB, modelos 55/65). Nada é gravado; devolve integrity.raw_sha256.
ImportarPOST /documents/importsmultipart file + preview_sha256 opcional. 201 criado, 200 deduplicado, 409 mesma chave com bytes diferentes, 422 inválido. Emite document.imported.
ExportarPOST /documents/exportsGET /documents/exports/{id}GET …/downloadZIP assíncrono com manifesto de hashes e XMLs originais; link temporário.

SDKs: documents.importPreview(), documents.import(), documents.exportRequest() (Node/PHP) e client.documents.import_preview(), import_xml(), export_request() (Python).

NF-e recebidas, manifestação e Carta de Correção #

Nada é transmitido à SEFAZ sem aprovação explícita e identidade conferida. A manifestação segue solicitar → aprovar (com o payload_hash devolvido) → transmitir; uma resposta ambígua exige reconciliar por consulta real, nunca reenvio cego. A CC-e é assíncrona e durável (no máximo 20 por NF-e).

OperaçãoMétodo / rotaNotas
RecebidasGET /nfe-distribuicao, GET /nfe-distribuicao/cursorDocumentos por NSU e o cursor por CNPJ/ambiente. Evento nfe.received por documento novo.
CapturaPOST /nfe-distribuicao/capturar; GET/PUT/DELETE …/captura-automaticaCaptura manual limitada ou opt-in automático por certificado e ambiente.
ManifestaçãoPOST /nfe-distribuicao/manifestacoes/{id}/aprovar/{id}/transmitir/{id}/reconciliarTipos 210200, 210210, 210220, 210240 (este exige justificativa). Eventos nfe.manifestacao.transmitted / rejected.
Carta de CorreçãoGET/POST /nfe/{id}/cceCorpo { correcao (15..1000), sequencial? (1..20) }. Eventos nfe.cce.registered / rejected.
Ator interessadoGET/POST /nfe/{id}/ator-interessadoEvento 110150. Corpo { documento (CPF/CNPJ), tp_autor?, tp_autorizacao?, sequencial? }. Eventos nfe.ator.registered / rejected.
Insucesso na entregaGET/POST /nfe/{id}/insucesso-entrega; POST …/{evento}/cancelarEventos 110192/110193. Corpo { dh_tentativa, tp_motivo (1..4), justificativa? (obrigatória se 4), n_tentativa?, latitude?, longitude?, imagem_base64? }; a imagem não é persistida (só o hash).
InutilizaçãoGET/POST /nfe/inutilizacoes, GET …/{id}Corpo { serie, numero_inicial, numero_final, justificativa (15..255), modelo?, ano? }. Assíncrona e durável; eventos nfe.inutilizacao.*.
Eventos por contratoGET /nfe/eventos/contratos; GET/POST /nfe/{id}/eventos; GET/POST /nfe/recebidas/{distribuicao}/eventosCatálogo oficial (conciliação financeira 110750/110751, cancelamento genérico, eventos RTC de IBS/CBS) com campos, autor (emitente/adquirente) e validação local antes da SEFAZ. Corpo { tipo, sequencial?, …campos do contrato }.

Homologação por modelo e rejeições explicadas #

Cada rejeição da SEFAZ chega com o texto oficial e a ação que resolve. A consulta da NF-e traz rejection com code, reason (xMotivo intacto), categoria, acao (corrigir_dados, certificado, reenviar, consultar, suporte) e dica. A homologação por modelo gera amostras XML/PDF assinadas com o seu certificado sem transmitir nada e sem consumir numeração.

OperaçãoMétodo / rotaNotas
Catálogo de rejeiçõesGET /nfe/rejeicoes, GET /nfe/rejeicoes/{cstat}Códigos fora do catálogo são classificados só pela faixa; nunca inventamos motivo.
Matriz de homologaçãoGET /nfe/homologacaoCenários por modelo (55: venda simples, multi-itens, destinatário contribuinte, payload inválido; 65: consumidor CPF, multi-pagamentos, payload inválido), UF e últimas corridas.
ExecutarPOST /nfe/homologacao/runCorpo { modelo (55|65), cenario?, mode? }. simulated (padrão): protocolo sintético marcado, numeração própria (999000001+), nada em nfes. sefaz: transmissão real ao ambiente de homologação, só com autorização no servidor (409 external_disabled). Limite 5/min.
AmostrasGET /nfe/homologacao/{id}/xml, …/pdfnfeProc com protocolo sintético (verAplic=FOXNFE_HOMOLOG_SIMULADA) e DANFE/DANFCe; cabeçalho X-FoxNFe-Homolog: simulated.

SDKs: nfe.rejeicao(), nfe.homologacaoRun(), nfe.homologacaoXml()/Pdf() (Node/PHP) e client.nfe.rejeicao(), homologacao_run() (Python).

Reforma tributária: IBS/CBS reproduzível #

Nenhum valor de IBS/CBS é estimado. A resolução usa a calculadora oficial sobre a parametrização aprovada pela sua empresa; sem regra aprovada a API bloqueia com um code estável. Toda resolução pode ser explicada e reexecutada; cenários versionados detectam deriva quando fontes ou regras mudam.

OperaçãoMétodo / rotaNotas
Status e classificaçõesGET /fiscal-agent/rtc/status, GET /fiscal-agent/rtc/classifications?dfe_type=&cst=Versão da calculadora e catálogo de classificações vigentes.
ResolverPOST /fiscal-agent/rtc/resolveCorpo { sku, dfe_type (NFE|NFCE|NFSE), tax_date, item_number, calculator_payload, … }. 422 com code: rule_missing, rule_ambiguous, classification_invalid, catalog_mismatch, payload_invalid. Os grupos oficiais (regular, monofasia, transferência de crédito, ajuste de competência, estorno, crédito presumido, compra governamental) entram no XML da NF-e por rtc_resolution_id no item.
Explicar e verificarGET /fiscal-agent/rtc/resolutions/{id}, POST …/{id}/verifyRegra, perfis, catálogo, versões e digest; verificação devolve reproducible, output_drift, version_drift ou unavailable.
CenáriosGET/POST /rtc-parameterization/scenarios, POST …/run, POST …/{id}/run, DELETE …/{id}Linha de base congelada na criação; cada execução devolve match, drift_classification, drift_output, version_drift ou rule_missing.

SDKs: classe Rtc (Node/PHP) e client.rtc (Python).

Cobertura NFS-e por município e provedor #

Publicamos só o que os drivers e os testes provam. Para cada município: provedor, tipo de integração, adesão ao padrão Nacional e o status de emitir, consultar, cancelar, substituir e consultar_lote (suportada, composta, nao_oferecida, nao_implementada), cada um com o arquivo de teste que o prova. Quando o provedor não tem integração, a emissão é recusada antes da rede (422) e cancelamento/substituição respondem 409 operacao_nao_oferecida.

OperaçãoMétodo / rotaNotas
MunicípiosGET /nfse/cobertura?uf=&q=&provider=&page=50 por página, ordenado por UF e nome.
DetalheGET /nfse/cobertura/{ibge}Capacidades com status, prova e nota por operação; homologacao_externa sempre pendente_autorizacao até o ciclo real.
ProvedoresGET /nfse/cobertura/provedoresMatriz por provedor e mapa de provas.

SDKs: nfse.cobertura(), nfse.coberturaMunicipio(), nfse.coberturaProvedores() (Node/PHP) e client.nfse.cobertura_municipio() (Python).

Status do serviço e casos de suporte #

OperaçãoMétodo / rotaNotas
StatusGET /status (sem token) e página /statusComponentes (banco, filas, cofre, disco, HTTP) com latência, incidentes abertos e simulações de contingência. Cache de 30 s.
Casos de suporteGET/POST /support/cases, GET …/{id}, GET …/slaCorpo { subject (3..200), priority? (urgent|high|normal|low), channel? }. SLA de primeira resposta/resolução por prioridade e aderência medida.

SDKs: classe Support (Node/PHP) e client.support (Python).

Estado dos domínios fiscais #

Publicamos somente o que está implantado e testado. Autenticação, webhooks, NF-e/NFC-e (com CC-e e contingência SVC), NFS-e Nacional, recebidas/manifestação, Central de Documentos e consultas auxiliares têm contrato disponível nesta página e nos SDKs 1.3.0. CT-e, MDF-e, NFCom, DC-e, NFGás e RetailSync têm contrato publicado em revisão — build, assinatura e validação contra o XSD oficial são reais, mas nenhum dos seis ainda transmite à SEFAZ; veja Expansão em revisão.

DomínioDisponibilidadeO que isso significa para você
Webhooks (CloudEvents + HMAC)DisponívelContrato publicado e documentado — veja Webhooks.
Autenticação e ambientesDisponívelCabeçalhos e fluxo documentados — veja Autenticação e ambientes. Os endpoints exigem autenticação (respondem 401 sem credenciais) sobre TLS.
NF-e / NFC-eDisponívelEmissão assíncrona, consulta, XML/DANFE, cancelamento, Carta de Correção (nfe/{id}/cce), recuperação durável e contingência SVC-AN/SVC-RS (modelo 55). EPEC/FS-DA e NFC-e off-line ficam estacionados e sinalizados por nfe.contingency. Outros eventos (ator interessado, insucesso na entrega, inutilização, conciliação financeira e eventos RTC), rejeições explicadas e homologação simulada por modelo — veja Homologação e rejeições.
NFS-e (Padrão Nacional)DisponívelEmissão, consulta, cancelamento e substituição com recuperação durável; cobertura por município/provedor é declarada, não presumida. Matriz pública de cobertura e recusa explícita antes da rede — veja Cobertura NFS-e.
NF-e recebidas e manifestaçãoDisponívelDistribuição DF-e com cursor, captura automática e manifestação do destinatário — veja NF-e recebidas.
Central de DocumentosDisponívelConsulta unificada, importação de XML com prévia e exportação ZIP — veja Central de Documentos.
Consultas auxiliaresDisponívelMunicípios, CFOP, CNAE, NCM, serviços nacionais, NBS, CEP e CNPJ — veja Consultas auxiliares.
Reforma tributária (IBS/CBS)DisponívelResolução oficial, explicação, verificação de reprodutibilidade e cenários — veja Reforma tributária.
Status e suporteDisponívelStatus público com incidentes e casos de suporte com SLA — veja Status e suporte.
CT-e, MDF-e, NFCom, DC-e, NFGás, RetailSyncParcialBuild, assinatura e validação XSD reais; sem transmissão SEFAZ ainda. Não integre em produção contra eles até saírem de Parcial — veja Expansão em revisão.

Homologação fiscal externa com certificado real é responsabilidade de cada integrador no ambiente de homologação (ambiente=2); esta página não substitui essa validação. Ela é atualizada a cada domínio que passa a disponível.

Expansão em revisão: CT-e, MDF-e, NFCom, DC-e, NFGás, RetailSync #

Seis domínios novos, todos com o mesmo limite hoje: build, assinatura ICP-Brasil e validação contra o XSD oficial são reais e testados; nenhum transmite à SEFAZ ainda. O documento gerado é assinado e válido contra o schema publicado do modelo correspondente, mas fica em status=validated — nunca authorized. Não use estas rotas para emissão fiscal real até a transmissão ser habilitada e anunciada aqui.

DomínioModeloRotaNota
CT-e57 (rodoviário)POST /ctes, GET /ctes/{id}Build/assinatura/validação XSD reais.
MDF-e58 (rodoviário)POST /mdfes, GET /mdfes/{id}Build/assinatura/validação XSD reais.
NFCom62POST /nfcoms, GET /nfcoms/{id}Um item por nota; ICMS via indSemCST.
DC-e99POST /dces, GET /dces/{id}Emitente/destinatário por CNPJ; transporte Correios.
NFGás76POST /nfgas, GET /nfgas/{id}Chave de acesso alfanumérica (CNPJ letras+números, IN RFB 2.229/2024) suportada nativamente.
RetailSyncnão fiscalPOST /retail-sync/events, GET /retail-sync/manifestIngestão idempotente de eventos de varejo offline (chave de retomada durável, sem expirar) e manifesto assinado por HMAC-SHA256. Não é um documento fiscal — reserva numeração modelo 65 para reconciliação posterior.

Todas as seis rotas exigem ability:nfes:emit e seguem o mesmo ciclo pending → processing → validated|rejected das demais emissões — nunca authorized nesta fase. Novidades por capacidade: veja Novidades.

Guias práticos #

Certificado A1

A emissão exige um certificado digital A1 (ICP-Brasil) válido, referenciado por certificate_id no payload. O certificado é carregado e validado no servidor; o evento certificate.validated sinaliza quando um certificado passou pela validação. Nunca envie o arquivo .pfx/.p12 nem a senha do certificado pelo front-end ou por canais de log.

Erros e novas tentativas

Erros de validação retornam 422 com um mapa errors por campo; falhas de autenticação, 401 ou 403. Para chamadas de escrita com resposta ambígua (timeout ou 5xx), não reemita cegamente: consulte o status do recurso e decida a partir do estado real. Respeite os limites de taxa (429) com espera antes de repetir.

Envios confirmados

Uma emissão só está confirmada quando o recurso sai de pending. Confirme por um destes caminhos: o webhook nfe.authorized/nfse.authorized, ou o polling do recurso até que status deixe de ser pending. O 202 inicial nunca é confirmação de autorização.

Reconciliação de entregas

Para garantir que nenhum evento foi perdido, cruze o seu registro local de eventos recebidos (deduplicados por id) com o histórico em GET /webhooks/deliveries. Entregas em falha terminal podem ser reenviadas manualmente pelo endpoint de resend; entregas presas antes da primeira tentativa são recolocadas na fila pela rotina interna de reconciliação. Como o modelo é at-least-once, a deduplicação no consumidor é parte obrigatória da integração.

Suporte #

Dúvidas de integração podem ser encaminhadas pelos canais oficiais da Central Fox: