Skip to main content

v2.21.0 (2026-09-11)

2026-09-11: espelho fiscal parado passa a alertar em vez de envelhecer calado

As tabelas do Cérebro Fiscal sincronizam por substituição integral, então toda sincronização bem-sucedida renova a data de atualização de todas as linhas. Quando uma delas falha, as guardas do motor preservam de propósito o espelho anterior — o dado não some, mas envelhece sem aviso. Agora um vigia confere diariamente a idade de cada espelho vindo do SAC e alerta acima de 48 horas, e o resumo de cada sincronização é lido na hora: tabela pulada ou com zero linhas vira alerta na mesma execução. tax_rule_icms fica de fora por não vir do SAC (tem base curada própria) e não gera alerta falso.

2026-09-11: login pelo painel volta a registrar o IP real de quem entrou

O painel autentica pelo servidor, não pelo navegador: quem abre a conexão com a API é o próprio painel. Por isso a auditoria registrava o endereço local em todo login feito pela tela — senha, Google e segundo fator —, e o alerta de login de superadmin saía sem dizer de onde. Agora o endereço do visitante é carregado adiante até a API, e Parceiros → Últimos logins volta a mostrar o IP verdadeiro. Quando não há origem confiável para ler, nada é inventado: fica registrado o endereço da conexão, nunca um valor escolhido por quem chamou. O bloqueio progressivo por tentativas erradas e o teto de tentativas por minuto também deixam de somar todos os usuários do painel no mesmo balde — antes um cliente podia ver “muitas tentativas” por causa do movimento dos outros.

2026-09-11: carta de correção pelo painel conta os 30 dias a partir da autorização

O prazo de 30 dias da Carta de Correção enviada pelo painel era medido pela data em que a nota foi criada no sistema, que pode ser bem anterior à autorização (reemissão, importação, fila). Agora conta a partir da autorização, como a regra fiscal e a própria mensagem do painel sempre disseram.

2026-09-11: itens da nota guardam quantidade e preço unitário com precisão exata

Quantidade e preço unitário dos itens passam a ser gravados como decimal exato, como já acontecia com os totais. O preço unitário não é arredondado: 50 × 0,015 continua 50 × 0,015, e o item fecha com o total.

2026-09-11: cadastro do segundo fator no login voltou a ser legível

Na tela de cadastro do segundo fator, título, texto de apoio e chave manual apareciam em azul-marinho sobre azul-marinho. O contraste do título foi de 1,30:1 para 16,95:1.

v2.20.0 (2026-09-09)

2026-09-09: Verificação em duas etapas, sessão curta e bloqueio de login

Contas com acesso privilegiado — superadmin e dono de parceiro — passam a exigir um aplicativo autenticador (TOTP de 6 dígitos) além da senha, com 10 códigos de recuperação de uso único. O access_token agora vale 12 horas e vem acompanhado de um refresh_token de 30 dias, rotacionado a cada uso; o logout revoga a sessão no servidor, e trocar a senha derruba todas as sessões abertas. Tentativas falhas de login bloqueiam progressivamente por e-mail e por IP, sempre com a mesma resposta 401 Credenciais inválidas.

2026-09-09: O CSC da NFC-e é cifrado antes de ser guardado

O CSC que você envia em POST /v1/companies ou PATCH /v1/companies/{id} é cifrado (AES-256-GCM) antes de chegar ao banco, amarrado ao emissor: o mesmo valor guardado para outro emissor não decifra. Ele nunca volta em nenhuma resposta — o GET do emissor não traz o campo — e nunca aparece em log. A partir desta versão, um emissor cujo CSC esteja guardado fora desse formato tem a emissão de NFC-e recusada com erro nomeado, em vez de seguir com o valor como está. Isso só alcança emissor cujo CSC tenha sido gravado por fora da API; quem cadastrou o CSC pelas rotas acima já está no formato cifrado e não precisa fazer nada. Se você receber essa recusa, reenvie o CSC por PATCH /v1/companies/{id}. O mesmo vale para a senha do certificado A1 enviada em POST /v1/companies/{id}/certificate.

2026-09-09: Qual IP a EngineAPI registra das suas chamadas

Toda chamada autenticada entra na trilha de auditoria do seu parceiro com o endereço de origem, e é esse endereço que aparece no painel e nos alertas de segurança. A API resolve o endereço a partir de X-Engine-Client-IP, X-Forwarded-For e X-Real-IP, mas apenas quando esses cabeçalhos chegam pelo gateway da EngineAPI. Se a sua integração enviar um desses cabeçalhos numa chamada direta, ele é descartado e vale o endereço real da conexão: não é possível escolher qual IP aparece na sua auditoria. Se você chama a EngineAPI através de um proxy próprio (NAT, saída fixa, gateway corporativo), o endereço registrado é o de saída desse proxy, não o da máquina interna.

O que dispara alerta de segurança

Estas situações avisam a equipe da EngineAPI na hora, mesmo quando a chamada é sua e legítima:
  • 20 ou mais respostas 401/403 do mesmo endereço em 5 minutos (normalmente é chave errada ou expirada em ambiente automatizado);
  • 3 ou mais cadastros criados do mesmo endereço em 1 hora;
  • gravação do destino do webhook (PATCH /v1/webhooks/config), rotação do secret (POST /v1/webhooks/secret/regenerate) e regeneração de chave de API.
Nenhuma delas bloqueia a chamada. Se a sua esteira de testes costuma bater em 401 em série, vale usar a chave ek_test_ e tratar o erro em vez de repetir a tentativa em laço.

2026-09-09: Política de conteúdo (CSP) do painel

Se você embute o painel da EngineAPI em um iframe, isso deixa de funcionar: app.engineapi.com.br passa a responder com Content-Security-Policy: frame-ancestors 'none' e X-Frame-Options: DENY. Não há como liberar por parceiro — o painel é uma superfície com sessão autenticada e enquadrá-la é o vetor clássico de clickjacking. Integração continua sendo pela API (api.engineapi.com.br), que não mudou de contrato.

HSTS em todos os hosts

Os hosts públicos da EngineAPI passam a mandar Strict-Transport-Security: max-age=63072000; includeSubDomains; preload. Na prática: depois da primeira visita, o browser recusa http:// para qualquer *.engineapi.com.br. Chamadas de servidor (SDK, curl, sua integração) não são afetadas — elas já usam https://.

v2.19.0 (2026-09-06)

2026-09-06: Onde pego o secret do webhook

O secret HMAC (whsec_...) só aparece completo em dois momentos: no PATCH /v1/webhooks/config que cria a webhookUrl pela primeira vez, e em todo POST /v1/webhooks/secret/regenerate. As duas respostas trazem revealedOnce: true. Guarde o valor na hora — não há rota que o recupere depois; regenerar invalida o anterior e mostra um novo.

Configuração e rotação do webhook exigem chave de produção

PATCH /v1/webhooks/config, POST /v1/webhooks/secret/regenerate e o retry/purge da Dead Letter Queue só aceitam o login do dashboard ou uma chave ek_live_. Uma chave ek_test_ recebe 403 AMBIENTE_DE_TESTE_SEM_ESCRITA_WEBHOOK: o secret HMAC é único por parceiro (não por ambiente), então uma integração de homologação rotacionando o secret invalidaria a assinatura que sua produção usa. Ler a configuração (GET /config), o histórico (GET /logs) e a DLQ (GET /dlq) continua liberado pra ek_test_.

2026-09-06: Resposta do webhook declarada no contrato

As respostas de configuração e rotação do secret do webhook agora têm schema público que declara revealedOnce.

v2.18.0 (2026-09-02)

2026-09-02: cadastro por CNPJ evita consultas repetidas ao provedor

A primeira consulta busca os dados cadastrais e as seguintes usam o cache permanente da engineAPI. Refresh exige motivo, datasets extras declaram o crédito consumido e a rota de saldo mostra apenas o consumo mensal do parceiro autenticado.

2026-09-02: PATCH de empresa recusa mudar ambiente em silêncio

PATCH /v1/companies/:id com sandbox ou ambienteFiscal diferente do atual devolve 422 AMBIENTE_IMUTAVEL e não escreve nada. Roundtrip do GET (valor igual) continua 200. Promover o emissor a SEFAZ real (sandbox: false) é PATCH /v1/admin/companies/:id/sandbox (SUPERADMIN), com certificado A1 válido; ambienteFiscal nessa rota não muda.

v2.17.0 (2026-08-31)

2026-08-31: cadastro por CNPJ evita consultas repetidas ao provedor

A primeira consulta busca os dados cadastrais e as seguintes usam o cache permanente da engineAPI. Refresh exige motivo, datasets extras declaram o crédito consumido e a rota de saldo mostra apenas o consumo mensal do parceiro autenticado.

2026-08-31: numeração automática reconcilia CStat 539 em vez de devolver 400 cru

Antes, emitir sem numero com o contador atrasado (restore, migração ou teste antigo) ia à SEFAZ, queimava o número e voltava 400 com o texto cru do CStat 539. O integrador tinha que adivinhar um número alto. Depois, o motor lê a chave conflitante na mensagem da SEFAZ, consulta a chave própria (não reemite às cegas), avança o contador e tenta de novo uma vez. Se a numeração era automática e o próximo número estiver livre, a emissão autoriza no mesmo request. Se não der, a resposta é 400 com code: SEQUENCIA_DESSINCRONIZADA e details.informeNumeroMaiorQue igual ao último nNF que a SEFAZ já tem nesta série — informe um numero maior que esse valor, ou omita numero e tente de novo. O webhook invoice.rejected dispara antes do 400, na fila e na NFC-e.

v2.16.1 (2026-08-30)

2026-08-30: Certificado vencido ou de outro CNPJ na régua de “pronto para emitir NFS-e”

O #911 fez prontoPara.nfse cobrar a presença do A1. Faltava a validade e a titularidade: certificado vencido (A1 dura 1 ano) ou emitido para outro CNPJ ainda lia "nfse": true, o FalaNota mostrava o botão e a falha só aparecia na assinatura da DPS. POST/GET/PATCH /v1/companies agora responde "nfse": false com nome próprio em faltando.nfse:
  • certificado — ausente (já existia)
  • certificadoVencidocertExpiry no passado
  • certificadoOutroCnpj — raiz (8 dígitos) do A1 diferente da do emissor. Filial com e-CNPJ da matriz não entra aqui.
Se o parse da validade falhar, avisos[] traz CERTIFICADO_VALIDADE_DESCONHECIDA (aviso, não recusa). A emissão recusa com 422 CERTIFICADO_VENCIDO ou 422 CERTIFICADO_OUTRO_CNPJ antes de transmitir. Nos 30 dias anteriores ao vencimento, avisos[] traz CERTIFICADO_EXPIRANDO — é aviso, não reprovação. Até o instante de expirar o emissor continua pronto; se vencer durante a ida à SEFIN, a recusa é do Fisco.

2026-08-30: Município não aderente no cadastro

POST/GET/PATCH /v1/companies passa a devolver avisos[] com MUNICIPIO_NAO_ADERENTE_PADRAO_NACIONAL só quando a cobertura é nao_aderente confirmada. desconhecido não gera aviso e o cadastro nunca falha por cobertura. Na emissão de NFS-e o mesmo código recusa em 422 antes de numerar, ou mapeia o E0037 da SEFIN preservando a mensagem original em details.

2026-08-30: avisos no SDK TypeScript

O tipo Company do @engineapi/sdk marcava avisos como opcional (avisos?), mas as respostas de POST/GET/PATCH /v1/companies sempre trazem o array — vazio quando não há aviso. O tipo passa a refletir isso (avisos: AvisoCadastro[]), então não é mais preciso tratar um undefined que a API não devolve. Nenhuma mudança de comportamento na API.

2026-08-30: Certificado na régua de “pronto para emitir NFS-e”

A emissão de NFS-e assina a DPS com o certificado A1 do emissor, mas prontoPara.nfse não olhava esse campo: emissor sem certificado lia "nfse": true no cadastro e só descobria o problema no erro de assinatura, depois de o número da DPS já ter sido alocado. POST/GET/PATCH /v1/companies agora responde "nfse": false com "certificado" em faltando.nfse sempre que o provider efetivo de NFS-e não for o mock, na mesma régua já aplicada a nfe/nfce. Na emissão, o mesmo cadastro recusa com 422 CERTIFICADO_AUSENTE antes de transmitir — nenhum nDPS é consumido. Emissor em sandbox continua emitindo sem certificado.

v2.16.0 (2026-08-28)

2026-08-28: Campos fiscais que ainda não emitimos agora dizem o porquê

Ao enviar Imposto Seletivo, retenções federais da NF-e, o valor aproximado de tributos da Lei da Transparência ou os grupos avançados de IBS/CBS, a resposta explica que o recurso ainda não existe, em vez de dizer apenas que o campo não foi reconhecido. Se houver caminho equivalente em outro documento, a mensagem indica qual é.

2026-08-28: NCM de gênero e CEP opcional deixam de bloquear emissões válidas

Itens de NF-e e NFC-e agora aceitam NCM com 2 dígitos nas situações previstas pelo leiaute, além do código completo de 8 dígitos. Na NF-e, o CEP do destinatário pode ser omitido; quando informado, continua documentado como 8 dígitos sem máscara.

2026-08-28: Aviso de validação: finalidade e origem da mercadoria

Valores que o contrato aceitava, mas que o leiaute oficial da NF-e/NFC-e rejeita, agora são recusados antes da numeração do documento. O erro identifica o campo e a faixa aceita. Valores fora dessas enumerações não formam um XML válido para autorização pela SEFAZ.

2026-08-28: Tamanho e enumeração do leiaute recusam na entrada

A API de NF-e e NFC-e deixa de aceitar valores que o XSD do leiaute 4.00 já recusaria depois da numeração: descrição acima de 120 caracteres, unidade acima de 6, CFOP com primeiro dígito 4, CNPJ/CPF com 12 ou 13 dígitos, tpNF fora de 0/1, CST IBS/CBS com menos de 3 dígitos, entre outros. A recusa é 400, nomeia o campo e a faixa, e não consome número fiscal. CEP com hífen continua válido. Finalidade da nota (finNFe) e origem do ICMS não entram nesta mudança.

2026-08-28: pastor de auto-merge pode ser exercitado sob demanda

O pastor da develop deixou de depender de um merge alheio para rodar. O botão manual aceita aviso ou ativo só naquela execução; sem input, o comportamento de hoje (PASTOR_ATIVO) não muda.

2026-08-28: timeout de emissão passa a ser reconciliado pela chave de acesso

Quando a SEFAZ recebe o documento mas a resposta se perde, a engineAPI consulta a chave persistida antes do envio. Documento já autorizado, cancelado ou rejeitado tem o estado recuperado sem uma segunda transmissão; somente uma chave não encontrada pode ser tentada novamente.

2026-08-28: Consumidor final derivado para não contribuinte

Em NF-e, indFinal continua opcional. Se vier omitido e o destinatário for não contribuinte, a API preenche 1. Declarar 0 contra esse destinatário agora falha em 422, com o número fiscal intacto.

2026-08-28: Cartão e PIX: informe tpIntegra antes de emitir

Migração: se você emite NF-e/NFC-e com forma "03", "04" ou "17", o payload agora precisa de pagamentos[].cartao.tpIntegra (1 integrado ou 2 não integrado). Sem isso a API responde 422 PAGAMENTO_SEM_DADOS_DO_MEIO antes de reservar número fiscal, no lugar da rejeição 391 da SEFAZ depois. PIX estático (chave copia-e-cola) usa { "tpIntegra": 2 }; CNPJ da credenciadora, bandeira e autorização são opcionais.

2026-08-28: Estados de entrega de webhook

O histórico de webhooks informa os estados de reenvio persistente e a DLQ permite acompanhar falhas.

2026-08-28: status de entrega de webhook

O histórico de webhooks agora pode informar retrying, processing e dead_letter, além dos estados anteriores. Use a DLQ para acompanhar ou reagendar falhas persistentes.

v2.15.0 (2026-08-22)

2026-08-22: NF-e deriva idDest do 1º dígito do CFOP

Antes, omitir idDest gravava sempre 1 (operação interna). A SEFAZ casa idDest com o 1º dígito do CFOP (B25a/B25b, rejeições 732/733), não com o par de UFs: CFOP 6102 sem idDest levava 732; derivar 2 da UF numa venda balcão 5102 levaria 733 e queimaria número. Agora a API deriva 1, 2 ou 3 do CFOP dos itens. Valor explícito vence sempre — o campo é declaratório. Itens mistos recusam 422 CFOP_IDDEST_DIVERGENTE nomeando os códigos, antes de reservar o número fiscal. A UF do emissor vem do cadastro da empresa. Na fila, o valor derivado entra só na chamada de emissão; o registro persistido guarda o que o parceiro mandou.

2026-08-22: sandbox sintético passa a emitir cNF válido no grupo ide

O XML sintético usado no emissor de demonstração passou a escrever cNF na segunda posição do ide, com 8 dígitos e diferente de nNF, como exige o leiaute. Com isso, a validação XSD do fixture do sandbox deixa de carregar o erro estrutural histórico e o teste agora cobra zero erro de schema.

2026-08-22: e-commerce B2C interestadual deixa de ser barrado por CEST

Venda interestadual a consumidor final não contribuinte (indFinal=1 e indIEDest=9) passa a resolver CST 00 no fluxo assistido mesmo quando o NCM tem CEST. Substituição tributária não incide nessa operação: a cadeia encerra no consumidor final, e o Fisco de SP manda CST 00 (RC 24282/2021). Exceção: faturamento direto de veículo novo no escopo do Conv. 51/00 (8429.59, 8433.59, 8702, 8703, 8704, 8705 e 8711, fontes Conv. 51/00 + Conv. 132/92 Anexo II + Conv. 52/93) continua em recusa de ST. Destinatário isento (indIEDest=2) não entra nessa porta. A partilha do DIFAL (grupo items[].icms.ufDestino) segue obrigatória.

2026-08-22: detector de ST ignora CEST de capítulo-inteiro do Anexo XXVI

NCM que só aparecia no CEST porque o Anexo XXVI cita o capítulo inteiro (ex.: 84224090 / 28.061.00 “Artigos de casa”) deixa de ser recusado por substituição tributária, em qualquer canal. CEST específico de verdade — eletrônico 21.057.00, bebidas do segmento 17 — segue recusando. A cláusula sétima §1º do Convênio ICMS 142/18 manda: quando a descrição não reproduz a NCM, só a descrição decide. Risco residual: porta-a-porta legítimo sairia com CST 00; a mitigação (flag no Issuer, opção 2) segue na fila do #633.

2026-08-22: /version volta a reportar a tag do release em produção

Antes, o endpoint podia responder um git describe antigo (por exemplo v2.11.0-39-g…) mesmo com a tag real do release já publicada. Depois, produção injeta a tag semântica do commit em execução — a que aponta para ele, ou a primeira release que o contém.

2026-08-22: sync de CEST corrige paginação inicial do SAC

O provider do SAC passou a iniciar a leitura em 0 no endpoint paginado de CEST e ganhou teste de regressão cobrindo um fluxo com duas páginas (0 e 1), evitando retorno silencioso da perda de dados no espelho.

2026-08-22: Regime Normal: exceção do canal porta-a-porta por emissor

A documentação pública passou a explicar a exceção do Convênio ICMS 45/99 para revenda porta-a-porta: com canalPortaAPorta=true no emissor, os CESTs de capítulo-inteiro do segmento 28 voltam a bloquear a emissão automática por ST em NF-e e NFC-e (ex.: venda de balcão interna no modelo 65). Com a flag desligada, permanece a regra global que não conclui ST por NCM nesses capítulos; na tripla DIFAL estrita, a tripla segue prevalecendo.

2026-08-22: sync de NCM corrige paginação inicial da tabela de redução

O provider fiscal passou a iniciar em page=0 a leitura paginada do NCM-Anexo-Reducao e ganhou teste de regressão cobrindo as páginas 0 e 1, evitando perda silenciosa da primeira fatia de regras da fonte.

2026-08-22: nItem na devolução (DFeReferenciado)

A NT 2025.002-RTC exige referenciar por item na devolução: cada det leva DFeReferenciado/chaveAcesso e nItem (número do item na nota original). Informe items[].documentoReferenciado.nItem (e opcionalmente chaveAcesso; senão usa referenciadas[0]). Se a venda original saiu por esta conta e cada cProd é único na original, a API deriva o nItem. Caso contrário recusa 422 DEVOLUCAO_SEM_NITEM antes de consumir número fiscal.

v2.14.0 (2026-08-15)

2026-08-15: preparação do release passa a caber em um comando

O carimbo de versão deixa de depender de três edições manuais. A preparação agora valida o trem, consolida os fragmentos e garante que o primeiro título deste canal seja a versão que o gate do release aceita.

2026-08-15: infraestrutura de release: back-merge automático

Todo merge na main agora volta pra develop automaticamente, mantendo a proteção de regressão de hotfix sem passo manual. Sem efeito na superfície da API.

2026-08-15: changelog por fragmento evita conflito entre entregas paralelas

Cada entrega passa a registrar sua mudança num arquivo próprio em changelog.d/. No release, os fragmentos são consolidados neste canal e no changelog do repositório antes do carimbo de versão. Assim, PRs paralelos deixam de disputar o topo dos mesmos dois arquivos.

2026-08-15: mensagens da guarda ICMS-ST e code de regime no CST 00

Três residuais da guarda de ICMS-ST, todos em 422 antes da numeração.
  • Payload misto. Campo de ST (modBCST/vBCST/pICMSST/vICMSST) e ICMS próprio (modBC/vBC/pICMS/vICMS) no mesmo item, sem CST 10/30/70, deixam de caber numa frase só. A ST aponta para os grupos de ST; o ICMS próprio aponta para os grupos em que o leiaute os declara (10/20/51/70/90).
  • Caminho assistido já ativo. A recusa do CST 00 manual com resolverTributacao: true passa a dizer que o motor passa a escrever o grupo depois que você remove o cst. O enrichment pula item com CST manual: “escreve o grupo sozinho” era cedo demais.
  • ⚠️ Contrato: CRT 1/4 com CST 00 e ICMS próprio. Esse payload deixa de devolver ICMS_ST_INVALIDO e passa a devolver CST_REGIME_INCOMPATIVEL — o mesmo code do Simples com CST 10/30/70. Quem roteia por code trata o caso como incompatibilidade de regime, não como forma do grupo de ST.

2026-08-15: formatos decimais fixos respeitam precisão e teto do leiaute

A validação numérica passa a usar as duas dimensões declaradas no dicionário: casas decimais e quantidade máxima de dígitos inteiros. Na NF-e e na NFC-e, campos 13v2 aceitam até 13 dígitos inteiros e 2 casas; na NFS-e, valores TSDec15V2 aceitam até 15 inteiros e 2 casas. Valor acima do teto devolve 400 com o caminho e o valor recebido, antes de reservar número fiscal. A seleção deixa de depender do prefixo monetário v. Por isso os pesos dos volumes transportados na NF-e, pesoLiquido e pesoBruto, passam a seguir o 12v3 do leiaute: no máximo 3 casas decimais, sem arredondamento silencioso. Formatos variáveis, como o valor unitário 11v0-10, conservam a faixa de casas e passam a respeitar o teto de dígitos inteiros. A tolerância de 4 ULPs continua absorvendo apenas resíduo de ponto flutuante na comparação das casas. Ela não aumenta o teto. A borda 99999999999.99 e o teto 9999999999999.99 do TDec_1302 permanecem aceitos; a regressão percorre 20.002 valores válidos de centavo nas bordas para impedir falsa rejeição.

v2.13.0 (2026-08-13)

2026-08-13: valores monetários de total recusam fração de centavo em vez de arredondar

Um valor como 1.005 num campo monetário de total (frete, desconto, seguro, valores de ICMS/ST/DIFAL/benefícios, pagamentos, troco, cobrança) era escrito no documento como 1.00, um centavo abaixo do que o payload declarou, sem aviso. Antes: a validação aceitava qualquer número e o documento saía com o valor arredondado. Depois: campo de total com mais de 2 casas decimais devolve 400 nomeando o campo, o valor recebido e a regra, antes de reservar número fiscal. Na NFS-e vale a mesma regra para servico.valorServicos (que também passa a recusar valor negativo), descontos, deduções e retenções. Dois detalhes de compatibilidade:
  • Ruído de representação binária não recusa. Resultado de conta em ponto flutuante, como 0.1 * 3 = 0.30000000000000004, é aceito e escrito como 0.30, em qualquer grandeza. A recusa é para frações reais de centavo, como 1.005 e 19.999.
  • Campos unitários não mudam. items[].valorUnitario continua aceitando até 10 casas decimais, conforme o leiaute.
O contrato OpenAPI dos campos de duas casas da NFS-e deixou de publicar multipleOf: 0.01: validadores de cliente aplicam essa regra com divisão em ponto flutuante e recusariam valores válidos como 4.35. O teto de casas está na descrição de cada campo.

2026-08-13: o contrato do complemento do tomador afirma o teto e as mensagens ficam específicas

O campo tomador.endereco.complemento da NFS-e sempre teve teto de 156 caracteres, mas o contrato OpenAPI publicado deixou de afirmá-lo quando o campo ganhou a normalização de vazio, e um valor de tipo errado devolvia a mensagem genérica Invalid input. Antes: o schema publicado trazia duas variantes do campo, uma sem maxLength, e complemento: 42 respondia sem dizer o tipo esperado. Depois: o contrato publica o teto e a descrição no campo único, o tipo errado responde nomeando campo e tipo (esperado texto, recebido número), e string composta só de espaços acima de 156 caracteres passa a ser recusada com 400 em vez de virar ausência (até 156, o comportamento de tratar como ausência continua igual).

v2.12.1 (2026-08-13)

2026-08-13: a recusa do ICMS próprio manual diz a causa real, caso a caso

Informar icms.cst: "00" com os campos do ICMS próprio (icms.modBC, icms.vBC, icms.pICMS, icms.vICMS) recebia uma única orientação: trocar para o CST 10/30/70. Para quem não cobra substituição tributária na operação, seguir a orientação mudaria a declaração fiscal do documento. A recusa continua acontecendo antes de qualquer número fiscal ser consumido, mas a resposta agora separa os casos:
  • Só campos do ICMS próprio com CST 00: o leiaute prevê os campos, e a engineAPI ainda não os escreve pelo caminho manual. A resposta manda remover os campos e aponta este changelog para acompanhar quando o cenário estiver coberto.
  • Qualquer campo de ST junto (icms.vBCST, icms.vICMSST, …): a orientação de informar CST 10/30/70 permanece, porque cobrar ST nesta operação é exatamente o que esses grupos declaram.
  • Emissor no Simples Nacional ou MEI: a resposta lembra que o regime declara icms.csosn, não icms.cst.
  • resolverTributacao: true já enviado: a resposta manda remover a declaração manual redundante; o caminho assistido escreve o grupo.

2026-08-12: validações de NFS-e passam a recusar valores que o documento não representa

O endereço do tomador agora trata complemento vazio como ausência: omitir, enviar "" ou enviar apenas espaços produz o mesmo documento sem complemento, e o catálogo público o mostra como opcional. Também ficaram mais estritos os valores que não poderiam chegar ao documento nacional. Antes, servico.aliquotaIss: 0.0250, endereço com travessão ou logradouro somente com espaços podiam atravessar a validação inicial e falhar tarde ou gerar documento inválido. Depois, cada caso devolve 400 com o nome do campo antes de reservar número da DPS. A alíquota aceita no máximo duas casas decimais e um dígito na parte inteira.

v2.12.0 (2026-08-12)

2026-08-12: declare à vista ou a prazo no pagamento da NF-e

pagamentos[].indicador entrou no contrato da NF-e: 0 é à vista, 1 é a prazo (a tag indPag do leiaute). O campo é opcional: sem ele, a tag continua fora do documento, como antes. A exceção é deliberada: NF-e com cobranca (fatura e duplicatas) passa a declarar 1 (a prazo) automaticamente, porque é o que a venda a prazo é; se você enviar um valor, o seu vence. Antes: enviar indicador era recusado com 400; a NF-e de venda a prazo saía sem a condição de pagamento declarada. Depois: o campo é aceito na NF-e (e continua fora da NFC-e, que tem regra própria). Guia: Venda a prazo.

v2.11.0 (2026-08-12)

2026-08-12: benefícios fiscais de ICMS emitem (redução de base, isenção, suspensão, diferimento e outras)

Empresa com benefício estadual passa a emitir informando os números do próprio ato concessório. O contrato de items[].icms ganhou os códigos de situação tributária 20 (redução de base), 40 (isenta), 41 (não tributada), 50 (suspensão), 51 (diferimento) e 90 (outras), para emissor em Regime Normal (crt: 3) ou no Simples com excesso de sublimite (crt: 2). Na NFC-e valem 20, 40 e 41: o leiaute do modelo 65 tem lista fechada de códigos, e os outros três recusam com 422 ICMS_BENEFICIO_NAO_SUPORTADO mandando emitir NF-e. Campos novos em items[].icms: modBC, pRedBC, cBenefRBC, o Fundo de Combate à Pobreza (vBCFCP, pFCP, vFCP), a desoneração (vICMSDeson, motDesICMS, indDeduzDeson) e o diferimento (vICMSOp, pDif, vICMSDif, pFCPDif, vFCPDif, vFCPEfet). Fora do imposto, dois grupos novos: items[].gCred (crédito presumido concedido pela UF, até 4 por item) e procRef na raiz do corpo (o processo ou ato concessório que ampara o benefício, até 100 por nota). Três coisas que valem a leitura antes de integrar:
  • A base do código 20 que você informa JÁ É a base reduzida. A API não aplica a redução por você, e confere valor contra base vezes alíquota com tolerância de um centavo.
  • indDeduzDeson é obrigatório sempre que vICMSDeson é informado. É ele que diz se o valor desonerado sai do total da nota, e o leiaute não define o que significa a ausência dele. Exigir é mais estrito que a norma, de propósito: com "1", o total transmitido sai reduzido e os pagamentos precisam fechar com esse total.
  • Os motivos de desoneração mudam com o código. Nos códigos 20 e 90 o leiaute admite 3, 9 e 12; nos 40, 41 e 50 admite 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16 e 90. Motivo fora do conjunto recusa listando os que valem ali.
Bloco pela metade recusa com 422 ICMS_BENEFICIO_INCOMPLETO e campo no código errado com 422 ICMS_BENEFICIO_INVALIDO, sempre antes de consumir número fiscal. O ICMS próprio destes grupos usa as tags do leiaute (vBC, pICMS, vICMS), o mesmo vocabulário da substituição tributária: os nomes clássicos seguem aceitos no ICMS sem benefício e no Simples, e junto de um código de benefício recusam, para o motor nunca escrever um nome e descartar o outro. Três recusas que valem a menção porque protegem o total da nota: uma nota não mistura indDeduzDeson "1" com "0" (o total sairia parcialmente deduzido, e não há regra confirmada para esse caso); item com indTot: 0 não declara desoneração dedutora (sai do valor de mercadoria e não pode reduzir o total); e vICMSDeson que arredonda para 0,00 recusa, porque o grupo inteiro sumiria do documento em silêncio. Quando a desoneração deduz do total, o valor registrado da nota sai reduzido junto com o vNF transmitido: o que você lê no webhook e na consulta é o mesmo número que foi ao documento. O que não mudou: a API continua sem descobrir sozinha que a sua operação tem benefício (o caminho assistido segue emitindo tributação integral) e sem conferir se o código de benefício existe na tabela da sua UF. Dos códigos, conferimos o formato que o leiaute exige. Detalhe completo no guia de benefícios de ICMS. Provado com emissão real autorizada em homologação no cst "20" (protocolo 152260027584995), com o XML conferido tag a tag: redução de base de 60%, base de R40,00eICMSdeR 40,00 e ICMS de R 7,20 no item, e o total do documento batendo com ele. Os demais códigos (40, 41, 50, 51 e 90) têm a estrutura validada contra o leiaute oficial e ainda aguardam a emissão real. ⚠️ Em Goiás, o cBenef é obrigatório no cst "20": sem ele a nota volta rejeitada com a rejeição 930, e o número fiscal já foi consumido. Informe o código da tabela de benefícios da sua UF no item.

2026-08-12: substituição tributária cobrada na sua operação (CST 10, 30 e 70) emite na NF-e

Quem RETÉM a ST agora emite. Indústria, atacado e distribuição que recolhem o imposto das etapas seguintes da cadeia usavam icms.cst "10", "30" ou "70" e recebiam 422 CST_NAO_SUPORTADO_NFE. Agora os três emitem na NF-e (modelo 55), para emissor em Regime Normal (crt: 3) ou no Simples com excesso de sublimite (crt: 2), pelo mesmo caminho que a revenda com ST já retida (cst: "60") usa desde a versão anterior: passthrough. Você informa os números, a engineAPI valida a forma e transmite. Opcionais: pMVAST, pRedBCST e os trios do Fundo de Combate à Pobreza (vBCFCP/pFCP/vFCP e vBCFCPST/pFCPST/vFCPST), cada um inteiro ou ausente. A margem de valor agregado e a alíquota interna de ST não são estimadas pela engineAPI: elas variam por estado, produto e convênio, e chutar qualquer uma delas seria inventar imposto em documento assinado. A base da ST também não é recalculada a partir da margem.
A ST compõe o total da nota. vNF = produtos − desconto + frete + seguro + outras despesas + IPI + ICMS-ST + FCP-ST. Numa venda de R100,00comR 100,00 com R 7,20 de ST, o pagamento é de R$ 107,20. Se pagamentos não fechar com esse total, a emissão recusa com 422 PAGAMENTO_DIVERGENTE antes de consumir número fiscal.
O que é conferido antes de emitir: vICMS contra vBC × pICMS (tolerância de R$ 0,01), que é a única conta com regra de validação escrita no leiaute; bloco pela metade (422 ICMS_ST_INCOMPLETO); e combinação que o documento não representa (422 ICMS_ST_INVALIDO), como pRedBC no CST "10" (esse campo só existe no "70"), ICMS próprio no CST "30", modalidade fora da tabela, ou alíquota acima de 100%. O valor da ST (vICMSST) não é conferido contra base vezes alíquota: não existe regra escrita para essa conta, e checar o que a norma não afirma recusaria emissão legítima. Os campos antigos icms.baseCalculoST, aliquotaST e valorST nunca chegaram ao documento e seguem recusados, agora apontando os nomes que emitem. Continuam fora, com recusa nomeada: desoneração do ICMS e da ST (vICMSDeson, motDesICMS, vICMSSTDeson), partilha entre estados (ICMSPart), repasse interestadual de ST em combustível e os códigos de ST do Simples (csosn 201/202/203). Na NFC-e os três códigos de ST não existem no leiaute: a emissão recusa antes de numerar e os campos devolvem 400. Provado com emissão real autorizada em homologação no cst "10" (protocolo 152260027584436), com o total do documento conferido tag a tag. Os cst "30" e "70" têm a estrutura validada contra o leiaute oficial e ainda aguardam a emissão real. Detalhe completo: Regime Normal e Cobertura fiscal.

2026-08-12: e-commerce interestadual emite com a partilha do ICMS (DIFAL)

A venda para outro estado, a consumidor final não contribuinte, passa a emitir com o grupo ICMSUFDest, para emissor fora do Simples Nacional. O Supremo, na ADI 5.464, suspendeu a exigência dessa partilha para os optantes do Simples (cobrá-la depende de lei complementar), então emissor com crt: 1 (Simples) ou crt: 4 (MEI) que informe o grupo recebe 422 DIFAL_NAO_APLICAVEL antes de numerar; o caminho vale para crt: 3 (Regime Normal) e crt: 2 (Simples com excesso de sublimite). O contrato ganhou items[].icms.ufDestino na NF-e (modelo 55), com os nomes do leiaute: vBCUFDest, pICMSUFDest, pICMSInter, pICMSInterPart, vICMSUFDest, vICMSUFRemet e, quando o estado de destino cobra o adicional de Fundo de Combate à Pobreza, vBCFCPUFDest, pFCPUFDest e vFCPUFDest. Os três somatórios do total da nota saem calculados dos itens. É passthrough: a engineAPI valida a forma e transmite, e não calcula a partilha. A base de cálculo e a alíquota internas aplicáveis são as da legislação do estado de DESTINO, que a engineAPI não tem curada. O que ela faz é recusar antes de consumir número fiscal o que o documento não representaria: 422 DIFAL_NAO_APLICAVEL quando o grupo vem fora da hipótese de partilha (a operação precisa ser interestadual, idDest: 2, a consumidor final, indFinal: 1, com destinatário não contribuinte, indicadorIE: 9 ou ausente), 422 DIFAL_INCOMPLETO quando o grupo vem pela metade (os seis campos obrigatórios e o bloco de FCP vão inteiros ou não vão) e 422 DIFAL_INVALIDO quando o número é incoerente: alíquota interestadual fora de 4/7/12, percentual de partilha diferente de 100 (integral para o destino desde 2019), parcela do remetente maior que zero e adicional de FCP que não fecha com base vezes percentual. Com resolverTributacao: true, informar o grupo destrava o cálculo: a mesma operação sem ele continua recusando com 422 TRIBUTACAO_NAO_RESOLVIDA, agora com a mensagem apontando o campo que resolve, em vez de anunciar uma limitação. Na NFC-e (modelo 65) o grupo não existe: aquele documento é sempre operação interna, e enviar o campo devolve 400 nomeando-o. Guia completo: DIFAL.

2026-08-10: o XML de uma emissão rejeitada agora é recuperável

GET /v1/nfe/xml/{id} devolve o XML transmitido de uma nota rejeitada. Antes a rota só alcançava documento autorizado, porque o caminho era derivado da chave de acesso, e emissão rejeitada não tem chave. Na prática, quem recebia uma recusa da SEFAZ ficava só com o código e o motivo, sem o documento para conferir. Agora a mesma rota serve os dois casos: com a chave de acesso (44 dígitos), o XML autorizado; com o id da nota, também o de uma rejeitada. É o documento assinado exatamente como foi enviado, então dá para conferir tag a tag o que foi recusado. Nada muda para quem já usa a chave de acesso.

2026-08-07: devolução, complementar e ajuste (finNFe 2, 3 e 4) emitem, com documento referenciado e forma Sem Pagamento

NF-e complementar, de ajuste ou de devolução (ide.finNFe: 2, 3 ou 4) emite. O contrato ganhou o documento fiscal referenciado: referenciadas[].chaveAcesso (array na raiz do corpo, chave de acesso de 44 dígitos do documento original) vira a tag do documento referenciado no leiaute, até 500 por nota. A chave aceita NF-e (55), NFC-e (65) e CF-e SAT (59). As três finalidades exigem o campo; sem ele a engineAPI recusa com 422 FINALIDADE_SEM_NFREF antes de consumir número fiscal, em vez de a nota sair rejeitada pela SEFAZ depois de o número já ter sido consumido (cStat 254 na complementar, cStat 321 na devolução). Em finNFe: 2 vale exatamente uma referência: a nota complementar complementa uma nota específica. A chave é conferida antes de emitir, e cada eixo tem 422 REFERENCIADA_INVALIDA próprio: dígito verificador pelo módulo 11, código de UF, ano/mês com mês entre 01 e 12, modelo, número do documento não zerado, chave repetida no array e referência à própria nota. O que a API não confere: CNPJ e UF embutidos na chave contra o emitente.
A forma de pagamento "90" (Sem Pagamento) também chega ao contrato. pagamentos: [{ "forma": "90", "valor": 0 }], única entrada e sem troco, documenta uma operação sem contraprestação (remessa, bonificação, comodato, devolução) e emite na NF-e (modelo 55). finNFe: 3 e 4 exigem essa forma: 422 FINALIDADE_EXIGE_SEM_PAGAMENTO se vier outra. Outra forma junto de "90", ou troco maior que zero, recusa com 422 SEM_PAGAMENTO_INVALIDO (não há pagamento do qual devolver troco). Só na NF-e: a NFC-e veda a forma "90" (422 SEM_PAGAMENTO_VEDADO_NFCE).
Mudança de comportamento, se você já integra: finNFe: 2/3/4 sem referenciadas deixa de ser recusado com o 422 FINALIDADE_SEM_NFREF genérico e passa a apontar o campo do contrato que resolve. Quem já enviava referenciadas (ou pagamentos com forma "90") e recebia recusa passa a emitir. finNFe ausente ou 1 (normal) segue sem nenhuma mudança.
Ver Cobertura fiscal e Erros e Rejeições.

v2.10.0 (2026-08-07)

2026-08-07: revenda de mercadoria com ICMS-ST já retido na NF-e (CST 60)

Quem revende mercadoria cuja Substituição Tributária já foi retida pelo fornecedor (bebida, autopeça, cosmético, medicamento) passa a ter caminho na NF-e. O item aceita icms.cst: "60" com os valores que vieram na nota de entrada, em items[].icms: vBCSTRet, pST e vICMSSTRet (base, alíquota suportada pelo consumidor final e valor retido), mais, quando houver, o FCP-ST retido (vBCFCPSTRet, pFCPSTRet, vFCPSTRet) e o ICMS efetivo (pRedBCEfet, vBCEfet, pICMSEfet, vICMSEfet). vICMSSubstituto é opcional e acompanha o primeiro bloco.
Nada é calculado: os números vêm do seu fornecedor e a engineAPI transcreve, recusando o que o documento não comporta. Cada bloco vai inteiro ou não vai: bloco pela metade recusa com 422 ICMS_ST_RETIDO_INCOMPLETO, e campo de ST retido sem o cst: "60" recusa com 422 ICMS_ST_RETIDO_INVALIDO, sempre antes de consumir número fiscal. O vFCPSTRet do total da nota passa a ser somado dos itens; o valor da nota (vNF) não muda. Vale para emissor em Regime Normal (crt: 3) e no Simples com excesso de sublimite (crt: 2), que são os dois regimes cujo documento carrega cst. Na NFC-e (modelo 65) esses onze campos não fazem parte do contrato: enviá-los devolve 400 nomeando o campo.
Mudança de comportamento consciente, se você já integra: item que traz icms.cst: "60" junto de icms.csosn, num emissor do Simples pleno (crt: 1) ou MEI (crt: 4), antes era aceito e saía 201: o cst era ignorado e o documento saía pelo csosn. Agora recusa com 422 CST_REGIME_INCOMPATIVEL. O cst deixou de ser um campo sem efeito, e aceitar os dois códigos no mesmo item seria aceitar duas tributações contraditórias e escolher uma em silêncio.Nesses regimes o documento carrega csosn, e o código equivalente (500) ainda não é emitido. Se o seu cadastro manda os dois no mesmo item, remova o cst: o desfecho volta a ser o de sempre, agora sem ambiguidade.O que não mudou: icms.cst: "60" sozinho nesses regimes já recusava com 422 CST_REGIME_INCOMPATIVEL e continua igual, só que agora a recusa sai na primeira tentativa, sem pedir antes que você complete campos que o documento do seu regime não carrega.
O lado SUBSTITUTO da cadeia (CST 10/30/70/90, imposto cobrado NESTA operação) continua recusando: ele depende de margem de valor agregado e alíquota interna de ST, que variam por UF, NCM e convênio, e a engineAPI não estima nenhuma das duas. Estado atual, com o que já emite e o que falta provar: Cobertura fiscal.

2026-08-07: desconto e dedução na NFS-e, e campos que passam a chegar ao documento

Prestador que dá desconto ou informa dedução volta a emitir. servico.descontoIncondicionado e servico.valorDeducoes eram escritos fora dos grupos que o leiaute da DPS exige, e o documento era recusado por schema depois que o número já tinha sido consumido. Agora o desconto sai em vDescCondIncond e a dedução em vDedRed/vDR, os grupos oficiais. O servico.descontoCondicionado passa a ter efeito no mesmo grupo. Vale lembrar a diferença que o leiaute faz: o desconto incondicionado e a dedução reduzem a base de cálculo do ISSQN, o condicionado não. Cinco campos que a API aceitava e não escrevia passam a chegar ao documento: servico.codigoNBS (cServ/cNBS), servico.codigoTributacaoMunicipio (cServ/cTribMun), tomador.inscricaoMunicipal (toma/IM), informacoesComplementares (serv/infoCompl/xInfComp) e o desconto condicionado acima.
Mudança de comportamento. Se você já envia algum desses cinco campos, a nota passa a sair com eles: até aqui a emissão respondia 201 e o documento saía sem o dado. Com resolverTributacao: true, o código NBS resolvido pelo Cérebro Fiscal também passa a constar no documento.Junto vieram validações que recusam antes de consumir numeração, em vez de deixar a rejeição chegar da SEFIN: NBS com 9 dígitos, código de tributação municipal com 3 dígitos (nos dois campos que o escrevem), inscrição municipal até 15 caracteres, informações complementares até 2000 caracteres e sem travessão, aspas curvas, reticências ou emoji (o leiaute só admite caracteres latinos básicos neste campo; quebra de linha é aceita e vira espaço), e desconto/dedução com no máximo 2 casas decimais.rps passa a ser recusado na emissão pelo Padrão Nacional (422 RPS_SEM_CAMPO_NO_PADRAO_NACIONAL): o leiaute da DPS não tem campo para número nem série de RPS, porque a própria DPS ocupa esse lugar. Antes o campo era aceito e a nota saía sem ele. Remova rps do payload e use o número da DPS devolvido na emissão para a sua correspondência.Códigos novos: RPS_SEM_CAMPO_NO_PADRAO_NACIONAL e CTRIBMUN_CONFLITO (os dois campos de código de tributação municipal informados com valores diferentes). Ver Erros.

v2.9.0 (2026-08-06)

2026-08-07: campo infCpl vazio não leva mais texto padrão pro documento

Quando informacoesComplementares não é preenchido, o campo infCpl some do documento em vez de trazer um texto padrão nosso. Antes, a NF-e e a NFC-e sem esse campo saíam com um aviso genérico impresso no DANFE/DANFCE do seu cliente. informacoesComplementares é opcional no leiaute: sem ele, a nota sai limpa, do jeito que o cliente final vê.
Mudança de comportamento, se você já integra: se o seu cadastro nunca preenchia informacoesComplementares, o documento até aqui saía com um texto padrão nosso nesse campo. A partir de agora, sem o campo, o documento simplesmente não traz mais nada ali.
A identificação do software emissor (verProc, campo técnico do leiaute) agora diz engineAPI. Antes saía com um nome antigo do produto. O crédito de quem gerou o documento continua aparecendo no PDF (DANFE/DANFCE), no rodapé da página, sem usar o campo fiscal.

2026-08-06: cBenef passa a ser aceito na NF-e e na NFC-e

items[].cBenef (código de benefício fiscal da UF) agora é aceito e vai para o documento. Antes, o campo era descartado e a nota podia sair sem ele, mesmo quando o cenário do item exige benefício fiscal informado, o que a SEFAZ recusa na validação. Formato de 8 ou 10 caracteres alfanuméricos, ou o literal SEM CBENEF; opcional, junto de icms.cst no mesmo item.

2026-08-05: ICMS monofásico de combustíveis (CST 02 e 61)

Revenda de combustível e de GLP passa a emitir. O ICMS de combustível não é percentual: é monofásico e ad rem, um valor em reais por unidade de medida, cobrado uma vez só na cadeia. O item agora aceita o grupo do leiaute em items[].icms: qBCMono, adRemICMS e vICMSMono (CST 02, tributação própria) ou qBCMonoRet, adRemICMSRet e vICMSMonoRet (CST 61, cobrada anteriormente, o caso da revenda). Vale nos dois regimes, inclusive Simples Nacional, e na NFC-e (onde o leiaute aceita só o CST 61).
Antes de emitir, a engineAPI confere o que a SEFAZ vai conferir: o valor tem que ser quantidade × alíquota ad rem, medido no número que vai no documento, e o item precisa do grupo combustivel com o código da ANP. Divergência devolve 422 sem consumir número fiscal. CST 15 (retenção) e 53 (diferimento) ainda não são emitidos, e dizem isso com 422 ICMS_MONOFASICO_NAO_SUPORTADO. O grupo combustivel chegou à NFC-e. Quem revende no balcão (posto, distribuidora de GLP) emite NFC-e, e agora pode informar o código da ANP no mesmo documento. Detalhe completo: Guia de combustíveis.

2026-08-05: CNAE de desenvolvimento de software resolve serviço padrão

Emissor com CNAE 6203-1/00 (desenvolvimento e licenciamento de software não customizável) agora resolve item da lista de serviços automaticamente no fluxo de resolução de serviço padrão da NFS-e, sem precisar informar o item manualmente. A tabela curada cobre hoje ocupações de MEI e este primeiro CNAE de empresa em geral; CNAEs fora da tabela seguem exigindo o item explícito no payload.

2026-08-05: venda a prazo com fatura e duplicatas na NF-e

A NF-e aceita o grupo cobranca (fatura e duplicatas do parcelamento). Venda B2B a prazo agora emite com o detalhamento financeiro completo: uma fatura (número, valor original, desconto e valor líquido) e uma lista de duplicatas (número, vencimento e valor), independentes entre si no leiaute (XSD). O campo é informativo: não altera o valor da nota nem a lista de pagamentos. Correção (2026-08-30, #930): apesar de estruturalmente independentes no XSD, duplicatas sem fatura nunca foi aceito pela SEFAZ na prática — a API passou a recusar essa combinação localmente, antes de numerar. Ver Guia: Venda a prazo.
Quando os dois vêm juntos, a soma das duplicatas precisa bater com o valor líquido da fatura, com 422 antes de qualquer envio à SEFAZ se não bater. Na NFC-e o campo é recusado com 422 (documento de balcão, sem faturamento a prazo). Guia completo: Venda a prazo.

2026-08-05: CSOSN do Simples Nacional recusa antes da SEFAZ, por regime e documento

csosn fora do que o motor emite corretamente agora recusa com 422 CSOSN_NAO_SUPORTADO, antes de qualquer chamada à SEFAZ. O conjunto que emite hoje é 102, 103, 300 e 400 (Simples), com domínio mais estreito para MEI. A tabela completa está em Erros e Rejeições e em Cobertura Fiscal.
Duas mudanças de comportamento conscientes, se você já integra:
  • csosn: "500" (ICMS já cobrado por Substituição Tributária) e csosn: "900" (categoria residual) agora recusam com 422. Os dois validam na forma do leiaute, mas exigem informação que a engineAPI ainda não resolve sozinha (CEST/CFOP para 500; resolução do destinatário para 900): emitir sem ela arriscaria um documento tecnicamente válido mas faticamente incorreto.
  • csosn aceito (102/103/300/400) junto com icms.aliquota, icms.baseCalculo ou icms.valor agora recusa com 422 CSOSN_VALORES_NAO_SUPORTADOS: nenhum desses campos existe no grupo do leiaute desses códigos, e antes eram descartados em silêncio.
Se o seu cadastro usa 500, 900 ou envia esses 3 campos junto com csosn, revise antes de atualizar em produção.
MEI (crt: 4) com csosn: "102" agora exige CFOP compatível (5102/ 6102 na NF-e; só 5102 na NFC-e). Fora disso, 422 CSOSN_CFOP_MEI_INCOMPATIVEL.

2026-08-05: retenções federais e de ISS com efeito real na NFS-e

retencoes passa a valer de verdade no documento da NFS-e (Padrão Nacional). Antes, o campo era recusado com 422 logo na entrada; agora vai para a DPS com efeito real: issRetidoPor (2 = retido pelo tomador, 3 = retido pelo intermediário), mais os valores retidos de inss, irrf, csll, pis e cofins, cada um no grupo do leiaute correspondente. O indicador de PIS/COFINS é derivado automaticamente do que você já informou (nunca precisa declarar um código adicional).
Combinações que o leiaute não permite (por exemplo, issRetidoPor divergente de um indicador de baixo nível já informado) recusam com 422 antes do envio. Detalhe completo por tributo: Erros e Rejeições.

2026-08-05: frete, seguro e outras despesas na NF-e e na NFC-e

O item aceita valorFrete, valorSeguro e outrasDespesas, com efeito real no documento. Os três valores somam no total da nota (indTot: 0 exclui o item específico dessa soma, sem afetar a tributação dele) e entram na base de cálculo do ICMS e do IBS/CBS, como manda a lei para frete cobrado pelo remetente e destacado em separado.
Valor negativo ou indTot fora de {0, 1} recusa com 422 FRETE_SEGURO_OUTRO_INVALIDO, antes de qualquer envio à SEFAZ.

2026-08-05: NF-e passa a declarar a forma de pagamento real

Cada entrada de pagamentos[] agora vai para o documento com a forma e o valor exatos que você enviou. Antes, toda NF-e saía declarando pagamento em dinheiro para a SEFAZ, mesmo quando o pagamento real era cartão, PIX ou boleto: o valor da nota estava certo, mas a forma declarada, não.
Mudança de comportamento, se você já integra: se o seu cadastro nunca informou pagamentos[].forma (campo já existia no contrato, mas era ignorado na prática), toda nota até aqui foi declarada como dinheiro à SEFAZ independentemente da forma real. Revise pagamentos[] no seu payload antes de assumir que está tudo certo.
troco (para pagamento em dinheiro com valor recebido maior que o total) também passa a ir para o documento, na mesma seção do pagamento.

2026-08-04: contrato de emissão mais estrito, cobertura fiscal documentada

Campo desconhecido no payload agora recusa com 400 acionável. Antes, um campo fora do contrato de emissão (por exemplo, um grupo do leiaute que a API ainda não suporta) era descartado antes de chegar à SEFAZ, e a nota podia sair autorizada sem o dado que você enviou. Agora todo objeto dos payloads de emissão (NF-e, NFC-e, NFS-e, lote e todos os sub-objetos) é estrito: campo desconhecido devolve 400 antes de qualquer efeito, sem número fiscal consumido, citando o campo, o objeto e os campos aceitos ali.
Nova página: Cobertura Fiscal. Antes de integrar, agora dá para responder “o cenário do meu cliente emite hoje?” sem abrir chamado: documento cruzado com regime tributário e operação, célula a célula, com o estado real (disponível, disponível em breve, não suportado) e o erro exato para os cenários ainda não suportados.

2026-08-04: combustíveis, inutilização de notas e mensagens de erro sem laço

Grupo de combustíveis (GLP) emite de verdade. NF-e com CFOP de combustível agora aceita e valida o grupo combustivel (código ANP, percentuais de GLP, vPart) contra o leiaute oficial. Sem o grupo, um CFOP de combustível recusa antes com 422 COMBUSTIVEL_GRUPO_OBRIGATORIO: a engineAPI nunca deixa a SEFAZ rejeitar depois de consumir o número fiscal. Detalhe completo: Guia de combustíveis. Inutilização de faixa de numeração para NF-e e NFC-e. Novo endpoint para inutilizar uma faixa de números não usados, com os mesmos limites de tamanho de justificativa e de tamanho de faixa por chamada nos dois documentos. Mensagens de erro pararam de se contradizer. Um mesmo cenário podia recusar com TRIBUTACAO_NAO_RESOLVIDA numa chamada e com CST_NAO_SUPORTADO na chamada seguinte, para o mesmo payload, sem caminho de saída. A mensagem agora é determinística e nomeia o motivo real na primeira resposta. Formato completo: Erros e Rejeições.