Validação e diagnósticos¶
Todo documento passa por um conjunto de regras antes de consumir numeração e antes de ir à SEFAZ. É de propósito: uma recusa nossa não queima número, enquanto uma rejeição da SEFAZ queima. Quanto mais cedo o erro aparece, menos lacuna você tem para inutilizar depois.
O que recusamos antes da SEFAZ¶
- Campos desconhecidos. Somos estritos: nós recusamos em voz alta um campo que não está no subconjunto suportado, em vez de ignorá-lo em silêncio. Isso é deliberado: se o seu sistema depende de um campo que ainda não mapeamos, você descobre no primeiro teste, e não no DANFE. O subconjunto suportado está na página de compatibilidade de cada dialeto.
- Consistência do documento. Os mesmos rejeitos que a SEFAZ aplicaria, verificados aqui: totais, tributos, CFOP, dados do consumidor, data de emissão.
- Série fora da faixa permitida para o modelo.
- CEST divergente do NCM. Um CEST que não corresponde ao NCM do item bloqueia, porque a SEFAZ rejeitaria. Um NCM que tem CEST associado, mas não o informa, apenas avisa. A operação pode legitimamente estar fora de substituição tributária.
- Grupo IBS/CBS ausente. Ver IBS/CBS é obrigatório.
IBS/CBS é obrigatório¶
Desde a transição de 2026, a SEFAZ rejeita qualquer NFC-e sem o grupo da reforma tributária (rejeição 1115, IBS/CBS não informado). A regra vale inclusive para documentos do Simples Nacional com CSOSN 102. Nós validamos a presença do grupo antes do envio, para poupar a viagem e o número.
Todo item precisa carregar o grupo:
No mínimo ibs_cbs_situacao_tributaria (CST, 3 dígitos) e
ibs_cbs_classificacao_tributaria (cClassTrib, 6 dígitos) por item. Informar só um
dos dois é erro de validação. Os valores (ibs_cbs_base_calculo, ibs_uf_aliquota,
ibs_uf_valor, ibs_mun_aliquota, ibs_mun_valor, ibs_valor_total, cbs_aliquota,
cbs_valor) acompanham quando a operação os exigir.
O grupo imposto.IBSCBS por item, com CST e cClassTrib. Os valores vão em
gIBSCBS (vBC, gIBSUF, gIBSMun, gCBS, vIBS). Se gIBSCBS for informado, as
três pernas (gIBSUF, gIBSMun, gCBS) devem vir juntas.
Nós calculamos os totais da reforma a partir dos grupos por item. Não os envie.
Como o motivo chega até você¶
Internamente, cada diagnóstico tem três partes: campo, problema e solução. Cada dialeto projeta isso no formato do provedor:
Recusa antes da SEFAZ vira 422:
{
"codigo": "erro_validacao",
"mensagem": "Erro de validação (Grupo IBS/CBS não informado no item.)",
"erros": [
{
"mensagem": "Grupo IBS/CBS não informado no item.",
"campo": "det[1].imposto.IBSCBS"
}
]
}
Rejeição da SEFAZ vira 201 com status: "erro_autorizacao", status_sefaz e
mensagem_sefaz. O código HTTP não distingue os dois desfechos na emissão, exatamente
como no provedor.
Recusa antes da SEFAZ vira um documento com status: "erro". Não há resposta da SEFAZ
para reportar, então codigo_status vem nulo e o bloco autorizacao carrega o
diagnóstico:
{
"status": "erro",
"autorizacao": {
"status": "erro",
"codigo_status": null,
"motivo_status": "Grupo IBS/CBS não informado no item.",
"mensagem": "Informe o grupo IBS/CBS do item (obrigatório desde 2026).",
"numero_protocolo": null,
"data_recebimento": null,
"chave_acesso": null
}
}
motivo_status carrega o problema e mensagem, a orientação. Rejeição da SEFAZ vira
status: "rejeitado" com codigo_status preenchido (inteiro).
Alguns cantos não têm resposta em banda: JSON malformado, id desconhecido, pré-condição de
operação não atendida. Neles respondemos com código HTTP honesto (404, 422, 502,
503) e um corpo mínimo. O contrato do ACBrAPI não descreve esses casos. O do FocusNFE
descreve. Cada página de compatibilidade lista o formato exato do seu dialeto.