Skip to main content

v2.27.0 (2026-09-19)

2026-09-19: PIS/COFINS por quantidade e IPI por pauta/unidade (combustível, bebida, cigarro)

Quem vende por unidade tributada (combustível, bebida, cigarro) usa PIS/COFINS com CST “03” (tributação por quantidade) e IPI tributado por pauta/unidade em vez de percentual. Até agora os dois recusavam com 422: o CST “03” de PIS/COFINS sempre, e o IPI só aceitava baseCalculo + aliquota. Agora o item aceita o par por quantidade, em vez do par por alíquota:
Três pontos:
  • A quantidade tributável não é a comercial. pis.quantidadeVendida/cofins.quantidadeVendida/ipi.quantidadeUnidadeTributavel são informados por você e podem divergir de items[].quantidade: a engineAPI é passthrough e não deriva um do outro.
  • Os dois grupos não convivem no mesmo item. Informar baseCalculo/aliquota (grupo por alíquota) junto de quantidadeVendida/valorAliquotaUnidade (grupo por quantidade), no mesmo pis/cofins/ipi, devolve 422 antes de numerar, citando o campo que não pertence ao grupo escolhido.
  • IPI continua fora da NFC-e. O leiaute do modelo 65 não tem grupo de IPI; ipi com qualquer conteúdo na NFC-e recusa com 422 IPI_NAO_SUPORTADO, como já acontecia com o IPI por alíquota.
Ver Cobertura Fiscal para a tabela completa de CST suportados.

v2.26.0 (2026-09-19)

2026-09-19: Substituição tributária no Simples Nacional (CSOSN 201/202/203)

Quem emite no Simples Nacional pleno (crt: 1) e vende mercadoria com substituição tributária não tinha caminho: o csosn de ST era recusado porque o contrato não expunha o grupo, e o cst de ST era recusado porque, nesse regime, o documento carrega csosn, não cst. Agora a NF-e (modelo 55) emite os três códigos.
O que cada código representa:
  • 201 — tributada com permissão de crédito e com cobrança de ICMS por ST.
  • 202 — tributada sem permissão de crédito e com cobrança de ICMS por ST.
  • 203 — isenção do ICMS na faixa de receita bruta, com cobrança de ICMS por ST.
É passthrough, a mesma doutrina do Regime Normal: a margem de valor agregado e a alíquota interna de ST variam por estado, produto e convênio, e a API não as estima — você informa os números, ela valida a forma e transmite, sem recalcular nada. O crédito do 201 (pCredSN/vCredICMSSN, art. 23 da LC 123/2006) também é seu: sai da sua apuração do Simples, e a API não o calcula. Três regras, todas verificadas antes de qualquer chamada à SEFAZ e sem consumir número fiscal:
  • O grupo da ST vai inteiro. Faltando modBCST, vBCST, pICMSST ou vICMSST, a emissão recusa com 422 ICMS_ST_INCOMPLETO — o que faltasse sairia como zero no documento.
  • O crédito é obrigatório no 201, e não existe no 202/203. Sem ele no 201, 422 ICMS_ST_INCOMPLETO: o documento sairia declarando crédito R$ 0,00 ao seu cliente. Informá-lo num 202/203, 422 ICMS_ST_INVALIDO: o leiaute não tem onde escrevê-lo ali.
  • Campo que o código não comporta recusa. ICMS próprio (modBC, vBC, pICMS, vICMS) não existe no grupo do Simples, e cst junto do csosn faria o documento sair pelo grupo do regime errado: 422 ICMS_ST_INVALIDO nos dois casos.
A ST compõe o total da nota também aqui: vNF = produtos + vICMSST + vFCPST − desconto + frete/seguro/despesas, e os pagamentos precisam fechar com esse total. O crédito do Simples não entra no total — é informação ao adquirente. Na NFC-e (modelo 65) os três códigos continuam fora: a regra de validação que lista os CSOSN aceitos naquele documento não traz os de ST cobrada na operação. Emita uma NF-e para essa operação. No MEI (crt: 4) também não: o domínio de CSOSN do MEI é mais estreito e não foi auditado para ST.

2026-09-19: Aviso quando sua classe derruba uma classe confirmada

A classe informada na emissão continua vencendo a classe confirmada do produto — isso não mudou, e a emissão nunca trava por causa disso. O que passou a acontecer é que a API guarda a classe derrubada e devolve o produto em GET /v1/fiscal/classification/pendentes com motivo igual a deslocou_confirmada, cClassTribAnterior e deslocouEm, para que alguém confira qual das duas classes vale. Confirmar a classe em POST /v1/fiscal/classification/classe limpa o marcador e tira o item da lista. Produto cuja classe anterior não estava confirmada não gera aviso, e classe confirmada reinformada igual segue confirmada e fora da lista. Antes: o override de uma classe confirmada não deixava rastro consultável. Depois: GET /v1/fiscal/classification/pendentes mostra o item com motivo igual a deslocou_confirmada e a classe anterior.

2026-09-19: CSC recusado na hora do cadastro, não na rejeição da nota

O CSC entra byte a byte no hash do QR-Code da NFC-e. Um espaço no fim, uma quebra de linha vinda de um copiar-e-colar ou um caractere invisível colado do portal mudam esse hash, e a SEFAZ devolve CStat 464 (Código de Hash no QR-Code difere do calculado) só depois que a nota já foi montada e enviada. Agora POST /v1/companies e PATCH /v1/companies/{id} recusam esses valores na entrada: csc aceita apenas caracteres imprimíveis sem espaço, e cscId apenas de 1 a 6 dígitos (é o cIdToken do leiaute). String vazia continua limpando o campo, como antes. E quando o 464 acontecer por outro motivo, ou seja, o par csc/cscId gravado não ser o que a SEFAZ tem registrado para o seu CNPJ, a mensagem de erro passa a dizer isso e a apontar o conserto, em vez de devolver só o texto da SEFAZ. Antes: CSC com sujeira invisível era aceito no cadastro e virava rejeição 464 opaca na emissão. Depois: recusa no cadastro, com a mensagem dizendo o que está errado; e o 464 explica a causa.

2026-09-19: Unidade tributável do item (vender em caixa, tributar por unidade)

O leiaute da NF-e carrega duas unidades para cada item: a comercial (como o produto é vendido — CX, FD, botijão) e a tributável (como o produto é tributado — UN, KG, L). Até agora a engineAPI escrevia a comercial nas duas, e quem vende no atacado não conseguia representar o documento real. Agora o item aceita os três campos, sempre juntos:
O documento sai com uCom=CX, qCom=2, vUnCom=120.00 e uTrib=UN, qTrib=24, vUnTrib=10.00. O valor do produto (vProd) continua vindo da unidade comercial: R$ 240,00. Três regras, todas verificadas antes de qualquer chamada à SEFAZ e sem consumir número fiscal:
  • Os três vão juntos. Informar um ou dois devolve 400 — metade do grupo emitiria uma nota com a unidade de um campo e a quantidade de outro.
  • Os dois lados têm que fechar. quantidadeTributavel × valorUnitarioTributavel tem que dar o mesmo valor que quantidade × valorUnitario. Divergente devolve 422 UNIDADE_TRIBUTAVEL_INVALIDA. Por isso valorUnitarioTributavel aceita até 10 casas decimais: uma caixa de 12 a R100,00vira8.3333333333porunidade,eeˊissoquefazaconversa~ofecharemR 100,00 vira `8.3333333333` por unidade, e é isso que faz a conversão fechar em R 100,00.
  • Mesma unidade exige os mesmos números. unidadeTributavel igual a unidade com quantidade ou valor diferentes devolve 422: sem troca de unidade não há o que converter.
Omitindo os três campos, o comportamento é o de sempre — o documento repete a unidade comercial nas duas leituras. Um efeito colateral bem-vindo: o GLP pode agora ser vendido por botijão (unidade: "UN") e tributado por quilo (unidadeTributavel: "kg"). A regra da SEFAZ que exige uTrib igual a kg para GLP (rejeição 854) passou a olhar a unidade tributável de verdade, não a comercial.

2026-09-19: Validação de WhatsApp no pedido de acesso

O campo whatsapp de POST /v1/auth/register passou a exigir DDD completo: 10 dígitos para telefone fixo ou 11 para celular (com o nono dígito). A validação ignora formatação, aceitando tanto dígitos crus (11988887777) quanto com máscara ((11) 98888-7777), mas rejeita telefone incompleto. Antes: "whatsapp": "11" era aceito. Depois: "whatsapp": "11" retorna 400 com a mensagem “WhatsApp inválido, informe DDD e número completo (10 ou 11 dígitos)“.

v2.25.0 (2026-09-17)

2026-09-17: NFC-e continua vendendo com a SEFAZ fora do ar

Quando a SEFAZ da UF do emissor está indisponível, a engineAPI emite a NFC-e em contingência offline (tpEmis 9) em vez de falhar. A nota volta assinada, com QR Code e DANFCE para o PDV imprimir e entregar ao consumidor na hora, e a engineAPI a transmite sozinha assim que o autorizador voltar, dentro do prazo legal de 24 horas. A contingência entra em três situações: você pede com contingencia: true; o monitoramento de disponibilidade das 27 UFs vê a SEFAZ do emissor fora do ar; ou a transmissão normal falha por infraestrutura e a consulta da chave responde que a nota não existe na SEFAZ. Uma rejeição fiscal nunca vira contingência, e contingencia: false desliga a decisão automática para uma nota específica. Nesse terceiro caso há dois desfechos que não geram contingência, e os dois protegem o seu número fiscal: se a consulta responder que a nota já está autorizada, a resposta é 201 com status: AUTHORIZED e o protocolo real da SEFAZ; se a consulta também não responder, a resposta é 503 CONTINGENCIA_SITUACAO_INDETERMINADA — a engineAPI não emite uma segunda nota com o mesmo número sem saber se a primeira foi autorizada, porque o cupom impresso nessa situação seria recusado depois. A nota fica na reconciliação automática e o desfecho chega por webhook; para continuar vendendo na hora, emita a próxima venda com contingencia: true. O QR Code de contingência segue o Manual de Padrões Técnicos do QR Code versão 2, e a engineAPI confere o formato antes de devolver a nota: QR fora do padrão recusa a emissão em vez de imprimir um cupom que o consumidor não consegue conferir. Na resposta, status é CONTINGENCIA_PENDENTE, protocol é null (protocolo só existe depois da transmissão) e o bloco contingencia informa o motivo, a justificativa impressa no documento e até quando há prazo. Dois webhooks novos acompanham o ciclo: invoice.contingency quando a nota é emitida offline e invoice.contingency.expired se as 24 horas passarem sem autorização. O desfecho final continua chegando em invoice.authorized ou invoice.rejected, com emitidaEmContingencia: true para o seu sistema distinguir uma autorização que chegou depois de a mercadoria já ter saído. O campo opcional justificativaContingencia (15 a 256 caracteres) vai impresso no documento fiscal; fora dessa faixa a resposta é 422 JUSTIFICATIVA_CONTINGENCIA_INVALIDA, antes de consumir qualquer número. Isto vale para o PDV que alcança a engineAPI e não consegue chegar à SEFAZ: um PDV sem internet nenhuma não é resolvido por API na nuvem.

2026-09-17: Reenviar uma nota que travou por falha de infraestrutura

Se a emissão parar por problema de infraestrutura — tempo esgotado, queda do processo, rede — e não por rejeição da SEFAZ, a nota fica em ERROR com a chave de acesso já gravada. A engineAPI grava a chave e marca TRANSMITTING antes de chamar o emissor, justamente para não perder o rastro quando isso acontece. Para reenviar, repita a mesma chamada com o mesmo numero:
  • POST /v1/nfe e POST /v1/nfce reconhecem a nota parada em CREATED, TRANSMITTING ou ERROR e retomam a mesma chave, em vez de recusar com 409 NUMERO_JA_UTILIZADO. O reenvio termina em AUTHORIZED ou REJECTED, como uma emissão comum: só a numeração e a chave vêm da tentativa anterior.
  • A retomada vale por 7 dias desde a criação da nota (o mesmo prazo da reconciliação automática). Passado esse prazo, o mesmo numero volta a recusar com 409 NUMERO_JA_UTILIZADO — reenviar um documento velho arriscaria rejeição de prazo na SEFAZ.
  • Se a chave gravada estiver em branco ou fora do formato de 44 dígitos, a nota não é retomada: a chamada recusa com 409 NUMERO_JA_UTILIZADO e diz o que fazer, em vez de devolver erro interno ou gerar uma chave diferente da que está gravada.
  • O reenvio não precisa repetir a nota anterior item por item: o que for enviado agora é o que vai para a SEFAZ e o que fica gravado na nota (valor, destinatário e itens). Só a numeração e a chave de acesso são herdadas da tentativa que falhou.
  • Se a chave gravada foi emitida em contingência que a retomada não sabe restaurar, a chamada recusa com 409 RETOMADA_CONTINGENCIA_NAO_RESTAURAVEL e pede contato com o suporte — nunca reenvia com uma chave diferente da gravada.
  • Reenviar nunca gera chave nova, nem com a SEFAZ fora do ar. Numa NFC-e reenviada, pedir "contingencia": true não produz mais um cupom de contingência, e a SEFAZ marcada como indisponível também não: o reenvio segue pelo caminho normal com a mesma chave. Antes, o reenvio virava contingência offline e gravava uma chave diferente por cima de uma nota que a SEFAZ podia já ter autorizado, e o consumidor levava um cupom que a SEFAZ depois recusaria. Para vender em contingência agora, emita a próxima venda com "contingencia": true: ela sai em contingência com número próprio.

2026-09-17: Chave de parceiro desativado responde 401

Quando um parceiro é desativado pela plataforma, as chaves de API dele (ek_live_ e ek_test_) são revogadas na mesma operação: qualquer chamada passa a responder 401, e o acesso ao painel também é recusado. Nada é apagado — documentos fiscais, emissores e a trilha de auditoria continuam no lugar, e um parceiro que já emitiu documento fiscal não pode ser desativado. Se suas chaves pararem de funcionar de uma vez, fale com o suporte antes de gerar novas.

2026-09-17: POST /auth/register não cria conta na hora

Este endpoint passou a ser um pedido de acesso antecipado: em vez de 201 com Partner + usuário + API Key de teste, ele responde 202 com { message, requestId }. Nosso time analisa o pedido e, se aprovar, você recebe um e-mail de convite para criar a senha; sua conta (com o plano Dev, R$0, já ativo) nasce nesse momento. O corpo da requisição também mudou: o campo password saiu, e entraram whatsapp, emits, volumePerMonth (opcional) e erpStack (opcional) — sem cnpj, que continua fora do cadastro público (anti-oráculo de enumeração); quem informa é o time, ao liberar o pedido. Veja o Quickstart atualizado.

2026-09-17: Valor aproximado dos tributos (Lei 12.741) por emissor

Você pode ligar, por empresa, o cálculo automático do valor aproximado dos tributos da Lei da Transparência: ligue valorAproximadoTributosEnabled no cadastro do emissor e toda NF-e e NFC-e passa a sair com o vTotTrib no item, o total no documento e a citação da fonte nas informações complementares. O valor é calculado pela engineAPI a partir do NCM do item, da UF do emissor e da origem da mercadoria — você não informa o número, e o campo continua fora do corpo da requisição. NCM que não existe na tabela não derruba a sua nota: ela sai sem o valor e sem a citação, porque a engineAPI não estima valor sem fonte e também não recusa emissão legítima por um código fora da tabela. Quem não liga a opção não muda em nada. Veja o guia Valor aproximado dos tributos (Lei 12.741).

2026-09-17: Quem escolheu a classe IBS/CBS do produto

O cadastro do produto guarda a classe de tributação IBS/CBS por emissor e código do item, junto com a origem dessa escolha: informado (veio no payload da emissão), confirmado (veio de POST /v1/fiscal/classification/classe), contador (o mesmo, feito por um usuário com papel de contador) ou pendente (as classes vigentes do NCM mudaram e a escolha precisa ser revista). A origem é um fato sobre quem decidiu a classe que está guardada agora, e por isso ela acompanha o valor:
  • Classe que a engineAPI resolveu sozinha — NCM com uma classe vigente só, ou reuso da classe já guardada — não é registrada como informada por você. Ninguém informou nada naquela chamada.
  • Classe que você mandou em items[].ibsCbs.cClassTrib é registrada como informado, mesmo quando substitui uma classe que estava como confirmado. Confirmar vale para a classe que foi confirmada, não para a próxima que chegar no lugar dela.
  • Mandar de novo a mesma classe que já estava confirmada não rebaixa nada: a origem continua confirmado (ou contador).
Isso importa porque GET /v1/fiscal/classification/pendentes e a revisão do contador se apoiam na origem. Se um override avulso do seu ERP herdasse o carimbo de confirmado, uma classe que ninguém auditou ficaria invisível na revisão — e seguiria valendo para todas as emissões seguintes daquele produto.

v2.24.0 (2026-09-16)

2026-09-16: administração convida o dono do parceiro

Ao criar um parceiro pela administração, informe ownerEmail para enviar o convite de OWNER válido por sete dias. Um SUPERADMIN também pode recuperar um parceiro sem dono por POST /v1/admin/partners/{id}/owner-invite.

2026-09-16: Percentual do Simples no cadastro ganha mês de referência

O cadastro do emissor aceita pTotTribSNCompetencia no formato AAAA-MM, junto do percentual padrão usado na NFS-e. Se a nota usar um percentual de mês anterior, futuro ou sem mês informado, ela continua sendo emitida e a resposta traz um aviso para confirmar o valor com o contador. Um valor enviado em dpsNacional.pTotTribSN continua prioritário; sem percentual no payload nem no cadastro, a resposta é 422 antes de transmitir.

2026-09-16: percentual do Simples no cadastro passa a ter mês de referência

O cadastro do emissor agora aceita pTotTribSNCompetencia no formato AAAA-MM, junto do percentual padrão do Simples. Se a NFS-e usar o percentual do cadastro com mês anterior, futuro ou sem mês informado, ela continua sendo emitida e a resposta traz um aviso para confirmar o valor com o contador; um valor enviado em dpsNacional.pTotTribSN continua tendo prioridade e passa sem alteração. A emissão ME/EPP sem percentual no payload nem no cadastro responde 422 antes de transmitir (antes, no caminho manual, respondia 400).

v2.23.0 (2026-09-14)

Administração: parceiros passam a nascer com convite para o dono

Ao criar um parceiro pela administração, informe ownerEmail. A pessoa dona recebe um convite válido por sete dias e define a própria senha ao aceitá-lo; a API não devolve mais uma senha temporária. Um SUPERADMIN também pode enviar um convite de dono para um parceiro sem membros, sem substituir um OWNER já existente.

2026-09-14: Changelog fiscal lista os ciclos dos últimos 90 dias

Quando o espelho muda classe ou percentual de um NCM, a página Changelog fiscal e GET /v1/fiscal/changelog mostram a data em Brasília, o catálogo, os tipos de mudança, os NCMs afetados e a decisão. Ciclo sem mudança não entra. Contagem de quem emitiu não é publicada.

v2.22.0 (2026-09-12)

2026-09-12: Regra fiscal alterada não muda mais a sua emissão sem aviso

O EngineAPI espelha as tabelas oficiais de tributação (IBS/CBS) da fonte e as republica para o motor de emissão. Quando a fonte alterava a regra de um NCM — uma classe de tributação nova, uma redução diferente —, o espelho copiava a alteração no ciclo seguinte e o tratamento tributário do seu produto mudava no mesmo dia. Agora, antes de gravar, o motor compara a publicação nova com a que está valendo e cruza o resultado com os NCMs que você emitiu nos últimos 90 dias. Se a alteração alcança algum deles, ela fica retida: a emissão continua com a regra anterior, e a mudança só entra depois de conferida e liberada por uma pessoa da plataforma. Nada muda no seu código: nenhuma rota, nenhum campo e nenhum contrato foram alterados. O que muda é a garantia — uma reclassificação na fonte deixa de virar, sem aviso, um 422 TRIBUTACAO_NAO_RESOLVIDA na sua próxima nota.

2026-09-12: a classe IBS/CBS fica no cadastro do produto

Quando o NCM aparece em mais de um anexo, a escolha de cClassTrib vale para o produto naquele emissor, não para cada nota. Informe a classe uma vez (no payload ou em POST /v1/fiscal/classification/classe); as próximas emissões reusam se a classe continuar entre as candidatas vigentes. Para listar as candidatas sem emitir: POST /v1/fiscal/classes-por-ncm. O 422 NCM_MULTICLASSE passa a ter entrada própria no catálogo de erros.

2026-09-12: changelog fiscal por ciclo

Quando o espelho do Cérebro Fiscal muda a classe ou o percentual de um NCM, isso passa a ter página própria: o que mudou, o 422 na emissão assistida, e um registro manual de ciclo. Alerta automático e histórico gerado por ciclo ainda não existem. Para NCM em mais de um anexo da LC 214/2025, a documentação separa o que o produto é (anexo/item) de como a operação acontece (diferimento, adquirente).

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.