Cenários de erro 400 (BadRequest) na emissão de NFS-e
Esta página lista os cenários em que a API de NFS-e responde HTTP 400 (BadRequest) ao emitir uma nota ou ao cancelá-la. Um 400 significa que a requisição foi rejeitada por validação de payload ou por regra de negócio — antes de a nota ser enviada à prefeitura.
Para a visão geral de todos os status HTTP (401, 408, 500, 503, 504), consulte Tipos de retorno de status das requisições HTTPS. Para diagnóstico passo a passo por sintoma, consulte Solução de erros 400 na emissão de NFS-e.
Como interpretar o corpo do erro 400
Existem dois tipos de erro 400, com corpos diferentes:
Validação de formato (desserialização do JSON)
Quando o corpo enviado não pode ser interpretado — JSON malformado ou tipo incompatível (por exemplo, enviar federalTaxNumber ou servicesAmount como texto não numérico) — a API responde 400 com um corpo ProblemDetails (JSON estruturado), antes de qualquer regra de negócio. As chaves de errors são o caminho do campo no JSON ($.campo):
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"$.borrower.federalTaxNumber": [
"The JSON value could not be converted to System.Int64. Path: $.borrower.federalTaxNumber | LineNumber: 0 | BytePositionInLine: 736."
]
},
"traceId": "00-<trace-id>-<span-id>-00"
}
Regras de negócio (validação e estado)
Os cenários das tabelas abaixo retornam um corpo de texto simples, em uma destas formas:
- Mensagem única — uma frase descrevendo a regra violada. Exemplo:
Municipal tax is not allowed to issue invoice. - Lista de mensagens — quando vários campos falham na mesma requisição, as mensagens vêm concatenadas (uma por regra violada).
- Sem corpo — alguns cenários retornam 400 com corpo vazio. Nesses casos, identifique a causa pela operação e pelo contexto da requisição (ver coluna "Mensagem retornada" marcada como sem corpo).
As mensagens de negócio são retornadas em inglês, exatamente como nas tabelas abaixo. Trate-as como literais ao exibir para o usuário final ou ao casar por texto em automações.
Validação do payload de emissão
Disparados ao validar o corpo do POST /v1/companies/{companyId}/serviceinvoices. Quando mais de uma regra falha, as mensagens são agregadas em uma única resposta.
| Campo / regra | Condição que dispara o 400 | Mensagem retornada | Como evitar |
|---|---|---|---|
Body | corpo da requisição nulo | Body can not be null | Envie um corpo JSON válido |
cityServiceCode | nulo ou vazio | cityServiceCode can not be null or empty | Informe o código de serviço municipal |
description | nula ou vazia | description can not be null or empty | Informe a descrição do serviço |
servicesAmount | < 0 | servicesAmount can not be less than 0.00 | Use valor de serviço >= 0 |
rpsSerialNumber | informado com comprimento 0 | rpsSerialNumber can not be less than zero | Omita o campo ou informe uma série não vazia |
rpsNumber | informado <= 0 | rpsNumber can not be less than or equal zero | Use número de RPS > 0 |
issRate | < 0 | issRate can not be less than 0.00 (0%) | Use alíquota entre 0% e 5% |
issRate | > 0,05 | issRate can not be more than 0.05 (5%) | Use alíquota <= 0.05 (5%) |
deductionsAmount | < 0 | deductionsAmount can not be less than zero | Use valor >= 0 |
discountConditionedAmount | < 0 | discountConditionedAmount can not be less than zero | Use valor >= 0 |
discountUnconditionedAmount | < 0 | discountUnconditionedAmount can not be less than zero | Use valor >= 0 |
irAmountWithheld | < 0 | irAmountWithheld can not be less than zero | Use valor >= 0 |
pisAmountWithheld | < 0 | pisAmountWithheld can not be less than zero | Use valor >= 0 |
cofinsAmountWithheld | < 0 | cofinsAmountWithheld can not be less than zero | Use valor >= 0 |
csllAmountWithheld | < 0 | csllAmountWithheld can not be less than zero | Use valor >= 0 |
inssAmountWithheld | < 0 | inssAmountWithheld can not be less than zero | Use valor >= 0 |
issAmountWithheld | < 0 | issAmountWithheld can not be less than zero | Use valor >= 0 |
othersAmountWithheld | < 0 | othersAmountWithheld can not be less than zero | Use valor >= 0 |
approximateTax.totalRate | < 0 | approximateTax.TotalRate can not be less than 0.00 (0%) | Use percentual >= 0 |
approximateTax.totalRate | > 99,99 | approximateTax.TotalRate can not be more than 99.99 (99.99%) | Use percentual <= 99.99 |
ibsCbs.classCode | informado e diferente de 6 dígitos | ibsCbs.classCode must be exactly 6 digits | Informe exatamente 6 dígitos |
ibsCbs.operationIndicator | informado e diferente de 6 dígitos | ibsCbs.operationIndicator must be exactly 6 digits | Informe exatamente 6 dígitos |
construction.encapsulationNumber | informado fora de 1 a 12 dígitos | construction.encapsulationNumber must contain from 1 to 12 digits | Informe de 1 a 12 dígitos |
additionalInformation | comprimento > 500 | additionalInformation length must be less than 500 characters | Limite o texto a 500 caracteres |
location.city | location presente com city nulo | location.city can not be null | Informe a cidade do local da prestação |
location.city.code | nulo ou vazio | location.city.code can not be null or empty | Informe o código IBGE da cidade |
location.city.code | diferente de 7 dígitos | location.city.code must be a valid IBGE city code | Use o código IBGE de 7 dígitos |
location.city.name | nula ou vazia | location.city.name can not be null or empty | Informe o nome da cidade |
location.state | nula ou vazia | location.state can not be null or empty | Informe a UF |
location.state | diferente de 2 caracteres | location.state must be a valid ISO 3166-2 code, samples (SP, RJ, AC) | Use a sigla da UF com 2 letras |
borrower.name | comprimento > 115 | borrower.name length must be less than 115 characters | Limite o nome a 115 caracteres |
borrower.federalTaxNumber | tomador no Brasil sem CPF nem CNPJ válido | borrower.federalTaxNumber borrower federal tax number is not valid | Informe CPF ou CNPJ válido |
borrower.federalTaxNumber | tipo NaturalPerson com CPF inválido | borrower.federalTaxNumber is not valid for natural person | Use CPF válido para pessoa física |
borrower.federalTaxNumber | tipo LegalEntity com CNPJ inválido | borrower.federalTaxNumber is not valid for legal person | Use CNPJ válido para pessoa jurídica |
borrower.phoneNumber | informado com menos de 7 dígitos | borrower.phoneNumber can not be less than 7 digits | Use telefone com 7 a 19 dígitos |
borrower.phoneNumber | informado com 20 dígitos ou mais | borrower.phoneNumber can not be longer than 20 digits | Use telefone com no máximo 19 dígitos |
Tomador estrangeiro (borrower.address.country diferente de BRA) pula a validação de CPF/CNPJ. Use o country correto para evitar rejeição indevida do documento fiscal.
A ausência do bloco borrower não retorna 400 na emissão. Ainda assim, informe o tomador: ele é necessário para a nota ser concluída — sem ele, a validação ocorre apenas adiante (consulta da nota/prefeitura).
Endereço do tomador (quando country = BRA)
Disparados por ValidateAddress. Um endereço totalmente vazio é aceito; ao informar qualquer campo, os demais passam a ser exigidos.
| Campo / regra | Condição que dispara o 400 | Mensagem retornada | Como evitar |
|---|---|---|---|
borrower.address.country | código fora da tabela ISO 3166-1 | borrower.address.country must be a valid ISO 3166-1 code, samples (BRA, ARG, COL) | Use o código de país de 3 letras |
borrower.address.postalCode | nulo ou vazio | borrower.address.postalCode can not be null or empty | Informe o CEP |
borrower.address.street | nulo ou vazio | borrower.address.street can not be null or empty | Informe o logradouro |
borrower.address.city | nula | borrower.address.city can not be null | Informe a cidade |
borrower.address.state | nula ou vazia | borrower.address.state can not be null or empty | Informe a UF |
borrower.address.state | diferente de 2 caracteres ou com espaço | borrower.address.state must be a valid ISO 3166-2 code, samples (SP, RJ, AC) | Use a sigla da UF com 2 letras |
borrower.address.city.code/name | código ou nome da cidade vazio | borrower.address.city.code or borrower.address.city.name can not be null or empty | Informe código IBGE e nome |
borrower.address.city.code | diferente de 7 dígitos | borrower.address.city.code must be a valid IBGE city code | Use o código IBGE de 7 dígitos |
borrower.address.state | UF inexistente na base | borrower.address.state is not a valid state code | Use uma UF válida |
borrower.address.city.code | cidade inexistente na base | borrower.address.city.code is not a valid city code or city | Use um código IBGE existente |
borrower.address.city.code + state | UF não corresponde ao prefixo do código IBGE | borrower.address.city.code and borrower.address.state aren't related. inform the correct state associated with correct city code | Garanta que cidade e UF sejam coerentes |
borrower.address.state | UF da base difere da informada | borrower.address.state from DB differs of city state informed | Garanta que cidade e UF sejam coerentes |
Comércio exterior (foreignTrade)
Validados apenas quando o bloco foreignTrade é informado.
| Campo / regra | Condição que dispara o 400 | Mensagem retornada | Como evitar |
|---|---|---|---|
foreignTrade.serviceMode | valor fora do enum | foreignTrade.serviceMode is invalid | Use um valor de enum válido |
foreignTrade.relationShip | valor fora do enum | foreignTrade.relationShip is invalid | Use um valor de enum válido |
foreignTrade.currency | nula ou vazia | foreignTrade.currency can not be null or empty | Informe a moeda |
foreignTrade.serviceAmountInCurrency | nulo | foreignTrade.serviceAmountInCurrency can not be null | Informe o valor em moeda estrangeira |
foreignTrade.serviceAmountInCurrency | < 0 | foreignTrade.serviceAmountInCurrency can not be less than zero | Use valor >= 0 |
foreignTrade.supportMechanismProvider | valor fora do enum | foreignTrade.supportMechanismProvider is invalid | Use um valor de enum válido |
foreignTrade.supportMechanismReceiver | valor fora do enum | foreignTrade.supportMechanismReceiver is invalid | Use um valor de enum válido |
foreignTrade.temporaryGoods | valor fora do enum | foreignTrade.temporaryGoods is invalid | Use um valor de enum válido |
foreignTrade.mdicDelivery | nulo | foreignTrade.mdicDelivery can not be null | Informe o indicador de entrega ao MDIC |
Regras de emissão
Disparados de forma síncrona depois da validação do payload, no POST /v1/companies/{companyId}/serviceinvoices. Quando a mensagem é sem corpo, identifique a causa pelo contexto da empresa.
| Cenário | Condição que dispara o 400 | Mensagem retornada |
|---|---|---|
| Imposto municipal inativo | habilitação fiscal da empresa diferente de "ativa" (em produção) | Municipal tax is not allowed to issue invoice |
| Empresa inativa | empresa com status diferente de "ativa" | company is not active |
| RPS duplicado | já existe nota com o mesmo número de RPS | service invoice with rps number ({rpsNumber}) already exists |
externalId duplicado | já existe nota com o mesmo externalId | service invoice with external id ({externalId}) already exists |
| Corpo da nota nulo | requisição sem dados da nota | sem corpo |
RPS ou externalId duplicado pode retornar 400 (e não 409). Não trate duplicidade assumindo um código de status fixo; verifique a mensagem retornada.
Outras validações (certificado digital, habilitação por código de serviço, regravação) ocorrem após o enfileiramento, no processamento assíncrono da nota. Elas não retornam 400 na chamada de emissão — o resultado é entregue na consulta da nota, no status e na mensagem de fluxo.
Cancelamento da nota
| Operação | Condição que dispara o 400 | Mensagem retornada |
|---|---|---|
Cancelamento (DELETE /{id}) | imposto municipal inativo (em produção) | Municipal tax is not allowed to issue invoice |
Veja também
- Tipos de retorno de status das requisições HTTPS
- Solução de erros 400 na emissão de NFS-e
- Campos de autorização NFSe
Para IA / LLMs
- Cobertura: cenários que fazem a API de NFS-e responder HTTP 400 (BadRequest) na emissão e no cancelamento da nota.
- Versão aplicável: API de NFS-e v1. Regras de validação são da plataforma NFE.io; a rejeição da prefeitura é posterior e não gera 400.
- Não parafrasear: a coluna "Mensagem retornada" é literal (retornada em inglês pela API). Reproduza a mensagem original ao responder; a coluna "Como evitar" pode ser interpretada.
- Corpo do erro: dois tipos. Validação de formato/desserialização → ProblemDetails JSON (
{ "errors": {...}, "status": 400 }). Regras de negócio → texto simples (mensagem única ou lista concatenada) ou vazio. - Síncrono vs assíncrono: só os cenários listados retornam 400 na chamada. Certificado, código de serviço e regravação ocorrem no processamento assíncrono e não viram 400 — chegam na consulta da nota.
- Pré-requisitos: Tipos de retorno de status das requisições HTTPS.
- Atualizado: 2026-06-27 por revisão humana.