# Erros e limites

> Prévia: a API abre junto com o painel.

Todo erro da API tem o mesmo formato, em português, com o que aconteceu e o que fazer:

```json
{
  "erro": {
    "codigo": "dados_invalidos",
    "mensagem": "A nota tem 1 campo para corrigir.",
    "como_resolver": "Corrija os campos de \"detalhes\" e envie de novo. Nada foi enviado à SEFIN.",
    "detalhes": [
      {
        "campo": "valores.aliquota_iss",
        "mensagem": "Alíquota do ISS: não informe quando o ISS não é retido (regra E0625).",
        "codigo_sefin": "E0625"
      }
    ],
    "requisicao_id": "req_01JC8Z5N3QK7T2V9X5B6N1R0QE"
  }
}
```

- `codigo`: estável, para o seu código decidir o que fazer. A lista está abaixo.
- `mensagem` e `como_resolver`: para mostrar a uma pessoa.
- `detalhes`: um item por campo, com o caminho (`tomador.documento`) e, quando a regra é da SEFIN, o
  código dela em `codigo_sefin`.
- `codigo_sefin`: quando a recusa veio da SEFIN, o código principal.
- `nota_id`: a nota envolvida, quando há (por exemplo, a nota rejeitada).
- `requisicao_id`: informe no chamado se precisar de ajuda.

## Recusa antes do envio e recusa da SEFIN

O NFSeIA confere as regras da SEFIN antes de enviar. O que dá para saber antes volta como
`400 dados_invalidos`: nada foi enviado, nada foi criado, nenhum número foi gasto. O que só a SEFIN
sabe (por exemplo, regras do município) volta como `422 nota_rejeitada`, e a nota fica guardada como
`rejeitada`, com os erros traduzidos. Nos dois casos, corrija e emita de novo com a mesma
`referencia_externa`.

`422` só sai quando a recusa é definitiva e conferida: o NFSeIA consulta a SEFIN pelo Id da DPS e só
marca `rejeitada` quando a nota comprovadamente não existe lá. Se a SEFIN disser que a DPS já existe
(E0014), se a consulta não for conclusiva ou se o XML não conferir, a resposta é `202` com a nota
`pendente` (motivo `conferindo_identidade`), e a referência continua presa a ela.

## Quando repetir

- **Pode repetir com a mesma `Idempotency-Key`:** `409 idempotencia_em_andamento`, `429`, `500` e
  `503`, depois do tempo de `Retry-After`. Essas respostas não ficam guardadas, e não há risco de
  nota em dobro: se a falha veio depois do envio à SEFIN, repetir devolve o estado atual da nota.
- **Corrija antes de repetir:** `400`, `401`, `402`, `403`, `404`, `409 idempotencia_reutilizada` e
  `422`. Repetir igual dá o mesmo erro.
- **`202` com nota pendente não é erro.** Não repita a emissão: a nota pode estar sendo autorizada.
  Espere o webhook ou consulte a nota.

## O que nunca aparece num erro

Um erro traz só os campos do formato acima. Nunca vão em resposta, erro ou conteúdo entregue à IA:
o corpo bruto da SEFIN, XML de DPS ou NFS-e, chaves, segredos ou qualquer material de certificado.
As mensagens da SEFIN chegam traduzidas, com o código em `codigo_sefin`.

## Códigos

Qualquer operação pode responder `401`, `403`, `429`, `500` e `503`, além das listadas na
[referência](https://nfseia.com.br/docs/referencia).

| HTTP | Código | Quando acontece | Como resolver |
| --- | --- | --- | --- |
| 400 | `requisicao_invalida` | JSON malformado, parâmetro de consulta fora do formato ou `Content-Type` diferente de `application/json`. | Confira o JSON e os parâmetros; o campo com problema vem em `detalhes`. |
| 400 | `idempotency_key_ausente` | POST que exige `Idempotency-Key` chegou sem ela. | Envie uma chave única por operação, por exemplo um UUID gerado a cada tentativa. |
| 400 | `dados_invalidos` | Os dados não passaram na validação antes de qualquer envio à SEFIN. Nada foi criado. | Corrija cada campo de `detalhes`. Quando a regra é da SEFIN, o código vem em `codigo_sefin` (por exemplo E0625). |
| 400 | `referencia_reservada` | A `referencia_externa` começa com `pagamento:`, `recorrencia:` ou `mcp:`, prefixos usados pelos gatilhos, pelas recorrências e pelo MCP. | Use o id do pedido, da fatura ou da cobrança do seu sistema, sem esses prefixos. |
| 401 | `chave_invalida` | Chave ausente, errada, revogada ou de outro ambiente. | Confira o cabeçalho `Authorization: Bearer nfs_...` e se a chave está ativa no painel. |
| 401 | `assinatura_invalida` | Aviso de pagamento sem `NFSeIA-Assinatura`, com assinatura que não confere ou com carimbo de tempo a mais de 5 minutos do horário atual, no passado ou no futuro. | Assine o corpo bruto com o segredo do gatilho, como em https://nfseia.com.br/docs/gatilhos. |
| 402 | `teste_encerrado` | O teste grátis acabou (5 notas reais ou 7 dias, o que veio primeiro). | Assine um plano no painel. O ambiente de teste continua funcionando. |
| 402 | `cota_esgotada` | A cota de notas do mês acabou. | Mude de plano no painel, em um clique, ou espere o próximo período. |
| 402 | `pagamento_pendente` | Assinatura em atraso; a emissão em produção fica suspensa. | Pague pelo painel. A emissão volta sozinha quando o pagamento é confirmado. |
| 403 | `sem_permissao` | A chave não tem o escopo que a operação pede. | Crie uma chave com o escopo listado em `detalhes`. |
| 403 | `credencial_do_mcp` | Uma credencial do MCP (chave `nfs_mcp_...` ou acesso por OAuth de uma IA) foi usada nesta API. Ela tem audiência própria e só vale no servidor MCP. | Crie uma chave de API (`nfs_test_...` ou `nfs_live_...`) no painel para o seu sistema. |
| 403 | `producao_nao_liberada` | A empresa ainda não liberou a emissão em produção. | Libere no painel; a liberação é confirmada pelo e-mail da empresa. |
| 403 | `certificado_indisponivel` | Sem certificado digital, certificado vencido, revogado ou de outro CNPJ. | Envie um certificado A1 válido da empresa pelo link de envio. |
| 403 | `valor_acima_do_limite` | O valor da nota passa do valor máximo por nota definido para a empresa. | Confira o valor ou aumente o limite no painel, com confirmação por e-mail. |
| 404 | `nao_encontrado` | O recurso não existe, é de outra empresa ou de outro ambiente. | Confira o id e se a chave é do mesmo ambiente em que o recurso foi criado. |
| 409 | `idempotencia_em_andamento` | Outra requisição com a mesma `Idempotency-Key` ainda está sendo processada. Esta resposta não fica guardada. | Espere o tempo de `Retry-After` e repita com a mesma chave. |
| 409 | `idempotencia_reutilizada` | A mesma `Idempotency-Key` veio com outro corpo, outra operação ou outro recurso alvo. | Use uma chave nova para cada operação diferente. |
| 409 | `estado_invalido` | A operação não cabe no estado atual, por exemplo cancelar nota pendente, baixar o XML de nota rejeitada ou mandar aviso a um gatilho pausado. | Consulte o recurso e confira o `status` antes de repetir. |
| 413 | `corpo_grande_demais` | O corpo passou de 64 KB. | Reduza o corpo; a descrição do serviço aceita até 2.000 caracteres. |
| 422 | `nota_rejeitada` | A SEFIN recusou a nota de forma definitiva, conferida pelo Id da DPS. A nota fica guardada com status `rejeitada` e a referência é liberada. | Corrija conforme `detalhes` e emita de novo com a mesma `referencia_externa` e uma `Idempotency-Key` nova. |
| 422 | `cancelamento_recusado` | A SEFIN recusou o cancelamento, por exemplo fora do prazo aceito pelo município. | Leia `detalhes`; fora do prazo, o caminho é falar com a prefeitura ou com o seu contador. |
| 429 | `limite_de_requisicoes` | A chave passou do limite por minuto ou por dia. | Espere os segundos de `Retry-After`. |
| 429 | `limite_diario_de_notas` | A empresa chegou ao teto de notas do dia. | Espere o dia seguinte (horário de Brasília) ou aumente o teto no painel. |
| 500 | `erro_interno` | Falha nossa. A nota nunca sai em dobro por causa disso. Se a falha veio antes de qualquer efeito, a resposta não fica guardada; se veio depois do envio à SEFIN, a operação continua e repetir devolve o estado dela. | Repita com a mesma `Idempotency-Key`. Se continuar, abra um chamado com o `requisicao_id`. |
| 503 | `indisponivel` | API em manutenção. | Espere os segundos de `Retry-After`. Se a SEFIN estiver fora, a emissão não dá 503; a nota fica `pendente`. |

## Limites

Os valores são os iniciais e podem subir com o uso; mudanças entram aqui antes de valer.

| Limite | Valor | Observação |
| --- | --- | --- |
| Requisições por chave | 120 por minuto e 20.000 por dia | Valores iniciais. Os cabeçalhos `RateLimit-*` mostram o que resta. |
| Emissões por empresa (API, MCP e gatilhos) | 30 por minuto | Notas recorrentes saem pela fila e não contam neste limite. |
| Notas por dia por empresa | Padrão igual à cota mensal do plano | Você pode baixar no painel, com confirmação por e-mail. É o teto de dano se a chave vazar. |
| Valor máximo por nota | Padrão de R$ 100.000,00 | Você muda no painel, com confirmação por e-mail. |
| Ambiente de teste | 1.000 notas por dia por empresa | Não conta na cota do plano nem no teste grátis. |
| Corpo da requisição | Até 64 KB | Maior que isso, `413`. |
| Espera pela SEFIN na emissão | Até 25 segundos | Passou disso, a resposta é `202` com a nota `pendente`. |
| Página de listagem | Até 100 itens | Padrão de 20. Use `proximo_cursor` para continuar. |
| Idempotency-Key | Até 255 caracteres, guardada por 24 horas | Vale para a empresa, o ambiente, o método, a operação e o recurso alvo. Depois de 24 horas, a mesma chave vale como nova; a `referencia_externa` continua protegendo a nota. |
| Reenvio de e-mail | 5 por nota por dia |  |
| Webhooks | Até 10 endereços por empresa e ambiente | Resposta em até 10 segundos; tentativas por até 24 horas. |
| Gatilhos | Até 20 por empresa e ambiente | O aviso vale até 5 minutos antes ou depois do carimbo de tempo; carimbo fora dessa janela, no passado ou no futuro, é recusado. |
| Avisos de pagamento por IP | 60 por minuto | Contado antes de conferir a assinatura, inclusive para avisos com assinatura inválida. |
| Avisos de pagamento por gatilho | 30 por minuto por gatilho, somados ao limite de emissões da empresa | Contado depois de a assinatura conferir. |
| Entrega de webhook | 10 segundos por tentativa, 64 KB lidos da resposta | Sem seguir redirecionamento; o endereço é resolvido e conferido a cada envio. |
| Consulta de CNPJ | 30 por minuto por chave |  |

Toda resposta traz os cabeçalhos do limite de requisições:

| Cabeçalho | Conteúdo |
| --- | --- |
| `RateLimit-Limit` | Requisições permitidas na janela atual. |
| `RateLimit-Remaining` | Quantas ainda restam. |
| `RateLimit-Reset` | Segundos até a janela recomeçar. |

Passou do limite, a resposta é `429`, com `Retry-After` em segundos:

```json
{
  "erro": {
    "codigo": "limite_de_requisicoes",
    "mensagem": "Esta chave passou de 120 requisições por minuto.",
    "como_resolver": "Espere 12 segundos (cabeçalho Retry-After) e tente de novo.",
    "detalhes": [],
    "requisicao_id": "req_01JC8Z6C6EA0H5J2M8R9C4F3EV"
  }
}
```

## Cota do plano

A cota de notas é por mês e conta só as notas de produção autorizadas. Ao chegar a 80% e a 95%, o
e-mail da empresa e o webhook `cota.perto_do_fim` avisam. Acabou a cota, a emissão volta
`402 cota_esgotada` até a troca de plano (em um clique, no painel) ou o mês seguinte. Não há cobrança
de excedente.
