Skip to main content
Toda resposta de erro da engineAPI segue o padrão RFC 7807 (Problem Details), aplicado globalmente pelo filtro global de exceções. Não existe o formato antigo {statusCode, error, message, sefazCode, sefazMessage}: esses campos não existem no contrato real.

Formato de Erro (RFC 7807)

Não existem sefazCode/sefazMessage no envelope. O código e a mensagem da SEFAZ vêm em error.erros[].codigo e error.erros[].descricao.

Timeout depois de emitir: consulte, não reemita

Um timeout não prova que a SEFAZ recusou ou deixou de receber o documento. A autorização pode ter acontecido e apenas a resposta ter sido perdida. Por isso:
  • consulte o mesmo recurso com GET /v1/nfe/{id} ou GET /v1/nfce/{id};
  • espere o webhook de autorização/rejeição;
  • não crie outra emissão com o mesmo pedido só porque o POST terminou em timeout.
A engineAPI usa a chave persistida antes do envio para reconciliar o estado. Só retransmite automaticamente quando a consulta retorna que o documento não foi encontrado; qualquer estado fiscal real encerra a tentativa.

Como o slug de error.type nasce

O último segmento de error.type (o <SLUG> em .../errors/<SLUG>) é resolvido nesta ordem, pelo filtro global de exceções:
  1. Se a exceção carrega um code (ou error) explícito e catalogado, entre eles os códigos de negócio (CERTIFICADO_AUSENTE, CNPJ_CONFLICT etc.), o slug é esse valor, normalizado (maiúsculas, espaços viram _).
  2. Sem code/error explícito, o slug cai num mapa fixo por status HTTP (400 vira BAD_REQUEST, 422 vira UNPROCESSABLE_ENTITY, 429 vira RATE_LIMIT_EXCEEDED etc.).
  3. Payload de validação em formato de array (rejeição da validação de schema, global a toda a API) sempre vira VALIDATION_ERROR, independente do status.
O filtro confere o slug no momento de enviar a resposta. Todo slug emitido pertence ao catálogo público de códigos de negócio ou à lista de códigos genéricos desta página. Se a exceção trouxer um código ausente do catálogo, a resposta conserva o status HTTP e usa RECUSA_NAO_CATALOGADA; a ocorrência é registrada para correção interna. O integrador pode tratar esse slug genérico sem depender do código inválido.

errors[] (validação) vs erros[] (fiscal): nunca os dois juntos

Os dois arrays de erro do envelope têm formato e origem diferentes e uma resposta carrega no máximo um dos dois:

null, "" e campo ausente no PATCH

O GET devolve null em todo campo que ainda não foi preenchido. Como a integração costuma ler o recurso, mudar um campo e devolver o objeto inteiro, o PATCH de emissor (PATCH /v1/companies/{id}) trata os três casos assim:
null só desliga a escrita daquele campo. Não afrouxa validação: valor não-nulo continua passando por DV do CNPJ, formato e limite de tamanho, e erro de conteúdo segue 400 com error.errors[]. sandbox e ambienteFiscal iguais ao atual (roundtrip do GET) são ignorados; valor diferente recusa com 422 AMBIENTE_IMUTAVEL, não é 200 silencioso.

Casas decimais em valores monetários (400)

Campos monetários de total da NF-e e da NFC-e cujo leiaute declara 2 casas decimais, como valorFrete, valorSeguro, desconto, outrasDespesas, valores de ICMS, ST, DIFAL e benefícios, pagamentos[].valor, troco e cobrança, aceitam no máximo 2 casas. Na NFS-e, a mesma regra vale para servico.valorServicos, descontos, deduções e retenções. Enviar uma fração de centavo devolve 400 antes de qualquer efeito. A engineAPI não arredonda frações de centavo intencionais nem transmite um centavo diferente do valor declarado. Só desconsidera o resíduo inevitável da representação binária de operações JavaScript, com tolerância proporcional à grandeza e muito menor que meio centavo.
valorUnitario é diferente: o tipo do leiaute permite até 10 casas decimais e essa precisão continua aceita. A mesma régua agora confere a parte inteira de todo formato decimal fixo. Um campo 13v2 aceita até 13 dígitos inteiros e 2 casas decimais; acima disso, a requisição devolve 400 antes de emitir. O teto exato 9999999999999.99 e a borda 99999999999.99 continuam válidos. A tolerância de ruído binário vale somente para a comparação das casas decimais e nunca aumenta esse teto. Campos numéricos fixos que não são dinheiro também seguem o dicionário. Na NF-e, transporte.volumes[].pesoLiquido e pesoBruto são 12v3: aceitam no máximo 3 casas decimais. Por exemplo, 1.234 é aceito e 1.2345 devolve 400 com o caminho do volume e a mensagem Campo de precisão fixa aceita no máximo 3 casas decimais. Recebido: 1.2345. Na NFS-e, os valores TSDec15V2 aceitam até 15 dígitos inteiros e 2 casas decimais. A largura também é derivada do dicionário da DPS, sem uma lista manual paralela. Formatos de precisão variável, como o 11v0-10 de valorUnitario, conservam sua faixa de casas; só o teto de 11 dígitos inteiros passa a ser aplicado.

Campo desconhecido no payload (400)

O contrato de emissão é estrito: campo que a engineAPI não conhece devolve 400 e nada é emitido (nenhum número fiscal é consumido, nenhuma fatura é criada). Vale para POST /v1/nfe, POST /v1/nfe/batch, POST /v1/nfce e POST /v1/nfse. A recusa acontece antes de qualquer processamento, inclusive quando você usa resolverTributacao: true.
Use caminho para localizar o campo no seu payload e motivo para decidir o que fazer: sem motivo, é nome errado ou campo que não existe (a mensagem sugere o campo aceito mais parecido, quando há um, e avisa quando a diferença é só a caixa das letras). Com motivo, leia a frase: ela diz se o grupo não existe ainda ou para onde ir.

Campos do leiaute que existem no contrato com outro nome

O corpo da requisição não é o XML. Alguns grupos existem, com nome de campo próprio:

Recursos fiscais suportados por passthrough

Estes grupos entram no documento depois de validados. A API verifica forma e coerência, mas não apura os valores nem consulta a regra da UF:

Campos do leiaute que a engineAPI ainda não suporta

Estes são campos reais da NF-e. Enviar qualquer um deles devolve 400 com o motivo específico, em vez de a nota sair sem o grupo:
A lista acima muda quando um grupo passa a ser suportado. O campo sai da recusa e entra no contrato, e a referência de campos passa a listá-lo.

O que muda para quem já integra

Se o seu payload usa apenas campos documentados, nada muda: mesma requisição, mesma resposta. Se você envia algum campo a mais, o que antes era ignorado em silêncio agora é 400. Foi uma decisão deliberada, e o motivo é o desfecho que o silêncio produzia: No lote (POST /v1/nfe/batch) a recusa é tudo ou nada: uma chave desconhecida em uma única nota recusa o envio inteiro com 400, e nenhuma nota é enfileirada. Antes o lote respondia 201 e descartava a chave. O caminho é o mesmo do singular: caminho em camposDesconhecidos diz a nota pelo índice (ex.: notas[2].items[0].comb). Checklist de migração:
  1. Rode seus payloads de homologação uma vez. Campo a mais aparece em details.camposDesconhecidos com o caminho exato.
  2. Remova os campos sem motivo (nome errado ou campo inexistente).
  3. Para os campos com motivo, o grupo ainda não é suportado: retire do payload e trate o cenário fora da API até o suporte existir.

Quando a SEFAZ rejeita: como ver o que foi transmitido

Recusa da SEFAZ vem com o código (cStat) e o motivo verbatim, mas quem depura precisa do documento que saiu daqui, não só da mensagem. O XML transmitido de uma emissão rejeitada fica guardado e é recuperável pelo id da nota:
A mesma rota serve os dois casos: com a chave de acesso (44 dígitos) devolve o XML autorizado; com o id, devolve também o de uma rejeitada, que não tem chave. É o documento assinado exatamente como foi enviado, então dá para conferir tag a tag o que a SEFAZ recusou.

finNFe 2, 3 ou 4 sem o documento referenciado (422)

ide.finNFe: 2 (complementar), 3 (ajuste) ou 4 (devolução) pressupõem, no leiaute, o documento fiscal referenciado apontando a nota original: sem ele o documento sai incompleto. A exigência é regra de validação de negócio da SEFAZ (tabela cStat), não uma restrição do XSD (o grupo é minOccurs="0" no schema): sem ele, a SEFAZ rejeitaria a nota depois de consumir o número, com cStat 254 na finalidade 2 e cStat 321 na finalidade 4 (a tabela não tem código dedicado para a 3). A engineAPI recusa as três antes, com 422, apontando o campo do contrato que resolve:
finNFe: 3 (ajuste) recusa com o mesmo code, mas sem cravar cStat: a mensagem diz “sem ele a SEFAZ rejeitaria a nota depois de a numeração já ter sido consumida”, porque a tabela cStat não tem entrada dedicada confirmada para essa finalidade. finNFe ausente ou 1 (normal) segue emitindo sem mudança. Ver Cobertura fiscal.

Documento referenciado inválido e a forma Sem Pagamento (422)

referenciadas[].chaveAcesso vira a tag do documento referenciado numa nota assinada, e a forma de pagamento "90" (Sem Pagamento) descreve uma operação sem contraprestação (remessa, bonificação, comodato, devolução). Quatro recusas cercam os dois campos, todas 422 e todas antes de consumir número fiscal. REFERENCIADA_INVALIDA: a chave de referenciadas[].chaveAcesso não tem 44 dígitos numéricos, o código de UF embutido nela não existe na tabela do IBGE, o ano/mês embutido não traz um mês entre 01 e 12, o modelo embutido não é NF-e (55), NFC-e (65) nem CF-e SAT (59), o número do documento embutido veio zerado, o dígito verificador não fecha pelo módulo 11, a mesma chave aparece duas vezes no array, o array tem mais de 500 entradas (o teto do grupo no leiaute), a chave aponta para a própria nota que está sendo emitida, ou finNFe: 2 (complementar) veio com mais de uma referência (a complementar complementa uma nota específica):
Chave repetida no mesmo array recusa com a mesma mensagem de campo, motivo diferente:
SEM_PAGAMENTO_INVALIDO: a forma "90" usada numa combinação que o leiaute não comporta: valor diferente de zero na entrada com forma "90" (a SEFAZ rejeita com o código 904), ou a forma "90" presente junto de outra forma de pagamento no mesmo documento (a forma descreve o documento inteiro, não convive com mais nenhuma entrada em pagamentos):
SEM_PAGAMENTO_VEDADO_NFCE: a forma "90" numa NFC-e (modelo 65). É vedação da SEFAZ, não limitação da engineAPI: a NFC-e registra venda a consumidor final com contraprestação; operação sem pagamento se documenta em NF-e (modelo 55):
FINALIDADE_EXIGE_SEM_PAGAMENTO: o inverso, finNFe: 3 (ajuste) ou 4 (devolução) com uma forma de pagamento diferente de "90". As duas finalidades registram a operação, não uma cobrança, e a SEFAZ rejeita a combinação com o código 871:
Uma devolução completa combina os três: finNFe: 4, referenciadas apontando a nota original, e pagamentos com uma única entrada, forma "90" e valor: 0. Ver Cobertura fiscal para o escopo exato de referenciadas (hoje só a chave de acesso de NF-e/NFC-e).

Códigos HTTP

Rejeição da SEFAZ/SEFIN é sempre 400, nunca 422. Todo 422 desta API acontece ANTES de qualquer chamada à SEFAZ/SEFIN/worker de emissão/provedor externo: a validação é sempre local. Não trate rejeição fiscal e 422 como sinônimos.

Catálogo de códigos de negócio

Todo código abaixo é o code que vira o slug de error.type (ver Como o slug nasce). A tabela é gerada da fonteapps/api/src/common/errors/catalogo-recusas.ts, o registro único de onde cada código é lançado — e cobre os codes de negócio ESTÁVEIS das famílias 400/402/403/409/422, fora dos slugs genéricos por status (BAD_REQUEST, UNAUTHORIZED, VALIDATION_ERROR etc., já cobertos em Códigos HTTP) e fora da rejeição SEFAZ/SEFIN (erros[], passthrough do Fisco — ver Rejeição SEFAZ/SEFIN). Código que você não reconhece: consulte a versão atual deste catálogo e mostre error.detail ao usuário. RECUSA_NAO_CATALOGADA indica que a API substituiu um código não registrado; informe error.requestId ao suporte. MUNICIPIO_NAO_ADERENTE_PADRAO_NACIONAL (422) é o mesmo código do aviso aditivo avisos[] no cadastro da empresa: município que não adere ao Padrão Nacional da NFS-e neste ambiente. Na emissão, recusa antes de numerar se o espelho já confirma nao_aderente; se a SEFIN ainda devolver E0037, a mensagem original segue em details.mensagemSefin. desconhecido nunca dispara este código.
CNPJ_CONFLICT sai com code e message idênticos nos dois casos (CNPJ seu ou de outro parceiro). A mensagem orienta GET /v1/companies e POST /v1/companies/transfer-request sem revelar o detentor (anti-oráculo, #383): a API nunca deixa um CNPJ de terceiro virar uma forma de descobrir se ele já tem emissor cadastrado em outro parceiro da plataforma, nem pela mensagem, nem pelo code.
Estes slugs também pertencem ao catálogo público, mas ficam fora da tabela gerada de recusas de negócio. O Doc Contract compara esta lista com o registro que o filtro usa na saída. EMISSOR_INEXISTENTE é um desfecho FAILED da fila, em resultData.error. Ele não é um slug de resposta HTTP: o emissor foi removido depois do enfileiramento e nenhum número fiscal foi consumido.
Os erros de consulta externa (GET /v1/queries/cnpj/:cnpj e GET /v1/queries/cpf/:cpf, Receita Federal/SerPro) que não batem com nenhum code de negócio caem nos slugs genéricos por status: NOT_FOUND (CNPJ/CPF não encontrado), BAD_GATEWAY (falha sem categoria mais específica) ou SERVICE_UNAVAILABLE (provedor fora do ar).

Rejeição SEFAZ/SEFIN (400): exemplos

Independente do módulo (NF-e, NFC-e, NFS-e), a rejeição fiscal chega como 400 com error.erros[]. Alguns códigos comuns:

Emissão assistida (422): quando aparece

Só ocorre com resolverTributacao: true no payload de NF-e/NFC-e/NFS-e. Se um campo fiscal obrigatório (ex.: CSOSN, grupo IBS/CBS, cTribNac) não tiver fonte para ser resolvido pelo Cérebro Fiscal, a API responde 422 e nada é emitido/persistido:
Na NF-e/NFC-e, o corpo real inclui {index, motivo} por item não-resolvido; na NFS-e, camposNaoResolvidos com {campo, motivo}. Corrija o cadastro do emissor (servicoPadraoLc116/cTribNacPadrao) ou informe o campo manualmente, e reenvie.

Espelho fiscal divergente

Um dos motivos possíveis dentro de details.itensNaoResolvidos: a redução de IBS/CBS que temos espelhada para o NCM não bate com a tabela oficial de classificação tributária para a classe (cClassTrib) daquele mesmo NCM. Recusamos com este 422 antes de transmitir: a SEFAZ rejeitaria a mesma incoerência com cStat 1034/1046/1063 (IBS-UF/IBS-Municipal/CBS), só que sem contexto nenhum. Como seguir agora: informe o grupo ibsCbs do item explicitamente (override do parceiro, o motor não recalcula nem consulta o espelho para ele), ou emita esse item sem resolverTributacao, com a tributação que o seu ERP já tem. Exemplo de payload, o motivo completo de recusar antes de transmitir, e como um mesmo NCM pode ter mais de um anexo de redução: Erros de IBS/CBS.

NCM multiclasse

Outro motivo dentro de details.itensNaoResolvidos, e o mais comum em alimento: o NCM aparece em mais de um anexo da LC 214/2025, com percentuais diferentes. Quem decide qual vale é o produto real, não o código: a resposta traz candidatas[] com todas as classes vigentes; informe ibsCbs.cClassTrib para desempatar. Exemplo completo de payload/resposta, a tabela dos 4 codes irmãos (NCM_MULTICLASSE, NCM_SEM_CLASSE_AUTOMATICA, CCLASSTRIB_INVALIDO_PARA_NCM, CCLASSTRIB_INADMISSIVEL_NO_MODELO) e as três regras de informar só a classe: Erros de IBS/CBS.

Pré-voo do emissor (422): quando aparece

Antes de acionar o worker de emissão, a engineAPI valida se o cadastro do emissor (NF-e e NFC-e, o mesmo cadastro) tem o que o documento fiscal exige. Duas checagens, mesmo contrato de resposta:
Faça upload do certificado A1 em Certificados no dashboard e reenvie.
Campos exigidos pelo XML da NF-e/NFC-e que o cadastro do emissor pode deixar vazio: IE (número de 2 a 14 dígitos ou a string "ISENTO", em MAIÚSCULAS, o pattern da SEFAZ é case-sensitive) e endereço completo (logradouro, bairro, município, UF, CEP). Sem eles, o schema local do motor fiscal reprovaria a nota; a engineAPI barra ANTES, com a lista exata do que falta:
Complete o campo faltante em Empresas e reenvie. camposFaltando[] lista TODOS os campos ausentes de uma vez (não corrige um por vez).O mesmo pré-voo protege a fila de emissão (POST /v1/nfe/batch): o item falha como FAILED permanente (sem retry, retry não completa cadastro) com este mesmo envelope em resultData.error, e nenhum número da sequência fiscal é consumido.
Exclusivo da NFC-e (modelo 65): fora do ambiente sandbox, csc (o token) e cscId (o ID do token) precisam estar cadastrados no emissor:
Gere/consulte o CSC no portal da SEFAZ do seu estado (Contribuinte → NFC-e → Autorização de Uso do CSC), cadastre em Dashboard → Emissores → aba NFC-e e reenvie. Emissor sandbox (toda conta nova nasce assim) emite normalmente sem CSC, só passa a ser exigido ao trocar para o ambiente Produção.

Outros códigos de negócio

POST /v1/nfe e POST /v1/nfce aceitam numero explícito no payload (passthrough). Se aquele número (na mesma série e modelo do emissor) já foi usado por outro documento, a resposta é 409 com code: NUMERO_JA_UTILIZADO: a mensagem diz o número/série/modelo em conflito e os dois caminhos: usar outro número, ou omitir o campo numero para a numeração automática do emissor (recomendado, o motor mantém a sequência fiscal atômica por emissor+série). Nada é emitido e nenhum número novo é consumido.
POST /v1/nfe e POST /v1/nfce com pagamentos[].forma "03" (crédito), "04" (débito) ou "17" (PIX) exigem o grupo pagamentos[].cartao com ao menos tpIntegra (1 integrado ou 2 não integrado). Sem ele, ou com tpIntegra=1 sem cnpjInstituicao, a SEFAZ rejeita com 391 depois de consumir o número fiscal (NT 2023.004 v1.11, RV YA04-10; GO aplica). A API recusa antes com 422 e code: PAGAMENTO_SEM_DADOS_DO_MEIO. PIX estático (chave copia-e-cola) usa { "tpIntegra": 2 }; CNPJ da credenciadora, bandeira e autorização são opcionais.
Na emissão de NF-e/NFC-e, a soma de pagamentos[].valor menos troco (se houver) precisa bater no centavo com o total dos itens (a mesma conta do vNF transmitido: soma dos vProd já arredondados a 2 casas). Divergência devolve 422 com code: PAGAMENTO_DIVERGENTE, citando os dois valores, antes de qualquer chamada à SEFAZ e sem consumir número fiscal.Com desconto nos itens, o total esperado é o LÍQUIDO (vNF = produtos − desconto + IPI): o desconto agora vai no documento, então pagar o valor bruto é divergência de verdade. Antes o motor aceitava os dois valores porque o desconto era ignorado na emissão; hoje não é mais.Com valorFrete/valorSeguro/outrasDespesas nos itens, eles SOMAM ao total (vNF = produtos − desconto + valorFrete + valorSeguro + outrasDespesas + IPI), o inverso do desconto. Não existe tolerância “sem os acessórios”: se você informa frete no item, o pagamento tem que cobri-lo.
items[].desconto é o desconto incondicional do item, em reais, o vDesc do leiaute. Ele vai para o documento (no item e no total), reduz o vNF e reduz a base de cálculo do ICMS e do IBS/CBS (LC 87/1996 art. 13, § 1º, II, “a”; LC 214/2025 art. 12, § 2º, III).Devolve 422 com code: DESCONTO_INVALIDO, antes de qualquer chamada à SEFAZ e sem consumir número fiscal, quando:desconto igual ao valor do item é aceito (item integralmente descontado: base 0, imposto 0). Desconto condicional (o que depende de evento posterior, como pagamento antecipado) não deve ser informado aqui: por lei ele integra a base de cálculo, e o leiaute do item não tem campo para ele.A recusa vale igual com e sem resolverTributacao: true, e nos dois caminhos de emissão (síncrono e lote); em nenhum deles o número fiscal é consumido.No sandbox, o XML de demonstração sai com os mesmos valores que a produção transmitiria (vDesc no item, vNF líquido): o que você testa é o que a SEFAZ receberia.
cobranca.fatura e cobranca.duplicatas[] (venda a prazo, Guia: Venda a prazo) passam por 5 conferências antes de qualquer chamada à SEFAZ, todas com code: COBRANCA_INVALIDA:
Fila (POST /v1/nfe/batch): uma nota pode ficar válida quando enfileirada e recusada quando processada, sem o payload ter mudado. vencimento >= hoje é conferido no momento em que o item roda o pré-voo, não no momento em que você enfileirou. Uma nota parada na fila (emissor sem certificado, atraso do worker) até depois da data de vencimento recusa no processamento: não é um payload ruim, é o tempo passando por baixo dele. Nesse caso específico, reenviar com a MESMA data de vencimento repete a recusa (ela só piora com o tempo); ajuste vencimento para uma data futura e reenvie.
items[].valorFrete, items[].valorSeguro e items[].outrasDespesas são vFrete/vSeg/vOutro do leiaute, em reais. Ao contrário do desconto, são campos aditivos: vão para o documento (no item e no total), aumentam o vNF e aumentam a base de cálculo do ICMS e do IBS/CBS, o mesmo mecanismo do desconto em sentido inverso (LC 87/1996 art. 13, § 1º, II, “a”/“b”; LC 214/2025 art. 12, § 1º, III).items[].indTot indica se o vProd do item entra no vProd/vNF do total da nota: 1 (ausente = 1, default do leiaute) compõe; 0 não compõe. Não muda a tributação do próprio item, só a composição do total. Na NFC-e só 1 é aceito (ver tabela abaixo).Devolve 422 com code: FRETE_SEGURO_OUTRO_INVALIDO, antes de qualquer chamada à SEFAZ e sem consumir número fiscal, quando:Pode informar desconto e valorFrete/valorSeguro/outrasDespesas no mesmo item (sem indTot: 0): a base de cálculo aplica os dois eixos juntos (vProd − desconto + valorFrete + valorSeguro + outrasDespesas).
Duas recusas de sanidade do [Total].vNF, sempre ANTES de qualquer chamada à SEFAZ e sem consumir número fiscal:Na prática, o guard por item de FRETE_SEGURO_OUTRO_INVALIDO (acima) já impede a maioria dos casos que levariam a um vNF negativo: este código é a rede de trás, para o caso de nenhum item compor o total.
Todo grupo tributário que você envia ou vai para o documento, ou é recusado com 422 antes de qualquer chamada à SEFAZ (nenhum número fiscal é consumido). Os valores informados de PIS/COFINS/IPI são transmitidos como vieram: o motor é passthrough e não calcula tributo.valor: 0 (zero explícito) não é erro em nenhum desses grupos, inclusive ICMS-ST e ipi: {}: o documento sai igual ao padrão zerado. Formatação também não é motivo de recusa: cst de 1 dígito vira 2 ("1""01") e cest com máscara é normalizado.Atenção ao IPI: ele compõe o total da nota (vNF = produtos + IPI). Para pagamentos, aceitamos tanto o total dos produtos quanto produtos + IPI: só o que não bate com nenhum dos dois devolve 422 PAGAMENTO_DIVERGENTE. O desconto dos itens, quando houver, é descontado dos dois valores (o documento sai pelo líquido).Os mesmos 422 valem no lote (POST /v1/nfe/batch): o item da fila falha no pré-voo, antes de consumir número fiscal, e sai como FAILED com o code, sem retry (payload não muda entre tentativas).
O bloco ICMS do item precisa ser coerente com o regime tributário do emissor (crt do cadastro):Todos são 422 locais: nada chega à SEFAZ e nenhum número fiscal é consumido.

Domínio efetivo de CSOSN, por (crt, modelo)

O motor não aceita todo código da Tabela CSOSN: só os que emite um documento correto de ponta a ponta, e isso depende do regime (crt) e do documento (NF-e ou NFC-e), não é uma lista única:201, 202 e 203 (substituição tributária cobrada nesta operação) emitem na NF-e do Simples Nacional pleno (crt: 1). Você informa o grupo inteiro: modBCST, vBCST, pICMSST e vICMSST (mais pMVAST e pRedBCST, opcionais, e o trio vBCFCPST/pFCPST/vFCPST, indivisível). O 201 exige também pCredSN e vCredICMSSN, o crédito do artigo 23 da LC 123/2006, que sai da sua apuração. O motor transcreve e valida: não estima margem nem alíquota interna de ST, e não calcula o crédito. Grupo incompleto recusa com 422 ICMS_ST_INCOMPLETO; campo que o código não comporta (ICMS próprio, cst junto do csosn) recusa com 422 ICMS_ST_INVALIDO. pCredSN e vCredICMSSN só têm lugar no 201: informá-los em qualquer outro código (um 202/203, um 102, ou um icms.cst de Regime Normal) recusa com 422 ICMS_ST_INVALIDO em vez de emitir sem eles. O leiaute não tem onde escrevê-los fora do grupo ICMSSN201, e uma nota autorizada sem o crédito que você informou é um crédito que o adquirente perde sem aviso. Na NFC-e os três continuam fora: a regra de validação que lista os CSOSN aceitos no modelo 65 não traz os códigos de ST, então emita uma NF-e para essa operação. No MEI também não: o domínio de CSOSN do MEI é mais estreito e não foi auditado para ST.101 (crédito) não emite em nenhum crt/modelo hoje: o leiaute exige pCredSN/vCredICMSSN num grupo que este contrato ainda não escreve, e emitir sem eles violaria o leiaute.500 (ICMS já cobrado por ST/antecipação) e 900 (categoria residual) são válidos na forma do leiaute, mas a Regra de Validação da NT 2024.001 (MOC 7.0, Anexo I) exige mais do que a forma: 500 precisa do CEST do produto e, na NFC-e, de um CFOP específico de retorno de ST; 900 depende do destinatário e do modelo de um jeito que o motor ainda não resolve sozinho. Emitir sem essas informações arriscaria um documento tecnicamente válido mas faticamente errado: a mesma régua “ST nunca chuta, recusa” do Regime Normal. Os dois recusam com 422 CSOSN_NAO_SUPORTADO hoje.MEI (crt: 4) com csosn: "102" exige CFOP específico: NF-e só 5102 ou 6102; NFC-e só 5102. Fora disso, 422 CSOSN_CFOP_MEI_INCOMPATIVEL (a SEFAZ rejeitaria a combinação com 337). CFOP ausente usa o padrão 5102, dentro do domínio.
Mesmo dentro do domínio da tabela acima, a NFC-e do Simples “pleno” (crt: 1) com csosn 103 ou 400 pode ser rejeitada pela SEFAZ da UF do emitente conforme a legislação estadual: a API não bloqueia esse caso hoje (não é regra federal única, varia por UF), mas avisa: teste na UF do seu emissor antes de ir a produção com esses códigos na NFC-e.
Regime Normal emite: veja o guia Regime Normal (Lucro Real/Presumido). O ICMS é calculado pelo motor fiscal a partir de uma base auditada, e o que ele não cobre recusa com 422 TRIBUTACAO_NAO_RESOLVIDA e motivo por item, inclusive produto sujeito a substituição tributária, detectado pelo NCM (CEST do Convênio 142/2018) antes de qualquer cálculo.Nesta fase (N1), o motor calcula o CST 00 (tributação integral). ST, benefícios fiscais e DIFAL têm caminhos manuais próprios: quem emite informa os valores, e a API valida e transmite os grupos aplicáveis. Fora desses caminhos, "cst" manual e "resolverTributacao": true recusam com motivo específico. Confira o que está coberto hoje em Cobertura Fiscal.
POST /v1/companies com um CNPJ que já existe como Issuer (o campo é único globalmente na base, não por parceiro) sempre volta 409 com o mesmo code e a mesma mensagem, não importa se o CNPJ já é seu ou de outro parceiro:
Mudança de contrato: até 2026-07-31 este 409 trazia dois codes distintos: CNPJ_CONFLICT_SAME_PARTNER (é seu) e CNPJ_CONFLICT_OTHER_PARTNER (é de outro parceiro). Isso anulava o anti-oráculo da mensagem única: o code diferenciado (visível em error.type) permitia enumerar quais CNPJs de terceiros já têm emissor cadastrado na plataforma. Os dois codes foram colapsados em CNPJ_CONFLICT; quem ramificava por code precisa atualizar para tratar um único valor.
Se o CNPJ é seu, o emissor já existe na sua conta, veja em GET /v1/companies. Se é de outro parceiro, use POST /v1/companies/transfer-request para solicitar a transferência (fila revisada manualmente pelo superadmin, com consentimento do dono atual). Ver Transferência de emissor.
Assinatura em PAST_DUE: requisições GET continuam funcionando (modo leitura), qualquer outro método volta 402:
Assinatura CANCELED (ou sem nenhuma assinatura) responde 403 (Forbidden), não 402. A distinção: 402 tem saída óbvia (pague e volte a emitir), 403 exige contratar/reativar um plano.
GET /v1/queries/cnpj/:cnpj consulta um provedor externo (Receita Federal) que tem seu próprio rate limit, independente do seu plano na engineAPI:
Distinto do 429/RATE_LIMIT_EXCEEDED da tabela de planos abaixo: ali é o teto do SEU plano na engineAPI; aqui é o teto do provedor de dados externo.

Estratégia de Retry

Implementando Retry com Backoff Exponencial


Rate Limits

Limites por plano, headers e o 429 (RFC 7807) estão em Rate Limits, fonte única, não duplicada aqui.

Veja também