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 com422: 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:
- A quantidade tributável não é a comercial.
pis.quantidadeVendida/cofins.quantidadeVendida/ipi.quantidadeUnidadeTributavelsão informados por você e podem divergir deitems[].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 dequantidadeVendida/valorAliquotaUnidade(grupo por quantidade), no mesmopis/cofins/ipi, devolve422antes 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;
ipicom qualquer conteúdo na NFC-e recusa com422 IPI_NAO_SUPORTADO, como já acontecia com o IPI por alíquota.
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.
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.
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,pICMSSTouvICMSST, a emissão recusa com422 ICMS_ST_INCOMPLETO— o que faltasse sairia como zero no documento. - O crédito é obrigatório no
201, e não existe no202/203. Sem ele no201,422 ICMS_ST_INCOMPLETO: o documento sairia declarando crédito R$ 0,00 ao seu cliente. Informá-lo num202/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, ecstjunto docsosnfaria o documento sair pelo grupo do regime errado:422 ICMS_ST_INVALIDOnos dois casos.
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 emGET /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 devolveCStat 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:
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 × valorUnitarioTributaveltem que dar o mesmo valor quequantidade × valorUnitario. Divergente devolve422 UNIDADE_TRIBUTAVEL_INVALIDA. Por issovalorUnitarioTributavelaceita até 10 casas decimais: uma caixa de 12 a R 100,00. - Mesma unidade exige os mesmos números.
unidadeTributaveligual aunidadecom quantidade ou valor diferentes devolve422: sem troca de unidade não há o que converter.
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 campowhatsapp 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 emERROR 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/nfeePOST /v1/nfcereconhecem a nota parada emCREATED,TRANSMITTINGouERRORe retomam a mesma chave, em vez de recusar com409 NUMERO_JA_UTILIZADO. O reenvio termina emAUTHORIZEDouREJECTED, 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
numerovolta a recusar com409 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_UTILIZADOe 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_RESTAURAVELe 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": truenã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: liguevalorAproximadoTributosEnabled 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 comoinformado, mesmo quando substitui uma classe que estava comoconfirmado. 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(oucontador).
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, informeownerEmail 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 aceitapTotTribSNCompetencia 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 aceitapTotTribSNCompetencia 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, informeownerEmail. 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 eGET /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, um422 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 decClassTrib 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, o422 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, eParceiros → Ú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. Oaccess_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 emPOST /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 deX-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/403do 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.
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 mandarStrict-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 declararevealedOnce.
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 semnumero 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 fezprontoPara.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)certificadoVencido—certExpiryno passadocertificadoOutroCnpj— raiz (8 dígitos) do A1 diferente da do emissor. Filial com e-CNPJ da matriz não entra aqui.
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, masprontoPara.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 dadevelop 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 informarretrying, 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, omitiridDest 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 escrevercNF 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 em0 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: comcanalPortaAPorta=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 empage=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: cadadet 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 emchangelog.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 CST10/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
00manual comresolverTributacao: truepassa a dizer que o motor passa a escrever o grupo depois que você remove ocst. O enrichment pula item com CST manual: “escreve o grupo sozinho” era cedo demais. - ⚠️ Contrato: CRT 1/4 com CST
00e ICMS próprio. Esse payload deixa de devolverICMS_ST_INVALIDOe passa a devolverCST_REGIME_INCOMPATIVEL— o mesmocodedo Simples com CST10/30/70. Quem roteia porcodetrata 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, campos13v2 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 como1.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 como0.30, em qualquer grandeza. A recusa é para frações reais de centavo, como1.005e19.999. - Campos unitários não mudam.
items[].valorUnitariocontinua aceitando até 10 casas decimais, conforme o leiaute.
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 campotomador.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
Informaricms.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 CST10/30/70permanece, 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ãoicms.cst. resolverTributacao: truejá 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 tratacomplemento 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 deitems[].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
20que você informa JÁ É a base reduzida. A API não aplica a redução por você, e conferevalorcontra base vezes alíquota com tolerância de um centavo. indDeduzDesoné obrigatório sempre quevICMSDesoné 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 ospagamentosprecisam fechar com esse total.- Os motivos de desoneração mudam com o código. Nos códigos
20e90o leiaute admite3,9e12; nos40,41e50admite1,3,4,5,6,7,8,9,10,11,16e90. Motivo fora do conjunto recusa listando os que valem ali.
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
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 usavamicms.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.
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 grupoICMSUFDest, 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.
"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).
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 aceitaicms.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.
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.
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.
v2.9.0 (2026-08-06)
2026-08-07: campo infCpl vazio não leva mais texto padrão pro documento
QuandoinformacoesComplementares 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ê.
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 emitems[].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).
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 CNAE6203-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 grupocobranca (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.
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.
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).
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 aceitavalorFrete, 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.
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 depagamentos[] 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.
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 com400 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.
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 grupocombustivel
(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.