Tratamento de erros
A API do FazNota retorna erros em duas formas distintas:
- Erros HTTP (
401,403,404,405,429) — autenticação, autorização e rate limit. - Erros de domínio (HTTP
200+body.status: "999") — validação ou erros internos.
Erros HTTP
401 Unauthorized
Token ausente, inválido ou expirado.
{ "error": { "status": 401, "message": "Token não autorizado." } }Ação: revise o header Authorization. Se o Token foi rotacionado, atualize.
403 Forbidden
Contrato inapto.
{ "error": { "status": 403, "message": "Este contrato está inapto..." } }Ação: entre em contato com o FazNota para regularizar o contrato.
404 Not Found
Recurso não existe (endpoint errado).
{ "error": { "status": 404, "message": "Resource not found." } }Ação: confira o path. Se for consulta por recibo, prefira o body.status: "500" (que vem com mais contexto).
405 Method Not Allowed
Método HTTP errado para o endpoint.
{ "error": { "status": 405, "message": "The specified HTTP method is not allowed..." } }Ação: confira se você está usando o verbo correto (GET, POST, PUT, DELETE).
429 Too Many Requests
Limite de consumo atingido.
{ "error": { "status": 429, "message": "Você atingiu o limite de consumo de API..." } }Ação:
- Pare de enviar requisições imediatamente.
- Leia o header
X-RateLimit-Reset(Unix timestamp) para saber quando retomar. - Implemente backoff exponencial em retries.
Erros de domínio (status: 999)
Sempre HTTP 200. O detalhe está em data.erro:
{ "status": "999", "descricao": "Ocorreu um erro interno com a nota.", "data": { "erro": "Campo nfe@chave deve ter 44 caracteres." }, "meta": { "request_id": "req_01HXY8ZG3J4K5M6N7P8Q9R0S1T", "timestamp": "2026-05-13T14:32:01.234Z" }}Mensagens de validação
Padrão: "Campo <recurso>@<campo> <regra>".
Exemplos comuns:
| Mensagem | Significado |
|---|---|
Campo nfe@chave não pode ser vazio. | Falta o campo. |
Campo nfe@chave deve ter 44 caracteres. | Tamanho incorreto. |
Campo nfe@serie inválido. | Não é numérico ou tem >3 dígitos (limite 1-999). |
Campo nfe@serie permite séries apenas entre 1 a 999. | Valor fora do range. |
Campo nfe@motivo deve ter entre 15 e 255 caracteres. | Texto curto/longo demais. |
Campo nfe@transporte@placa-veiculo inválido. | Não bate com regex AAA-9999 ou Mercosul. |
Campo nfe@cliente documento não pertence a um cliente cadastrado. | CPF/CNPJ não está cadastrado. |
Campo nfe@itens não pode ser vazio. | Array vazio. |
Mensagens de erro interno
Mensagens não-controladas (vindas de exceções inesperadas) são substituídas por uma frase genérica para não vazar detalhes da infraestrutura:
{ "status": "999", "data": { "erro": "Erro interno ao processar a requisição. Tente novamente em instantes ou entre em contato com o suporte informando o request-id." }, "meta": { "request_id": "req_..." }}Status terminais que não são “erro técnico” mas falha de negócio
Estes códigos não são erros da API, mas indicam que a operação não deu certo. Trate-os no nível de negócio:
status | Significado | Tratamento |
|---|---|---|
050 | Duplicidade de numero-origem | Não é erro; é proteção. Use o recibo retornado. |
500 | Recibo não existe | Verifique se você salvou o recibo correto. |
900 | Rejeição SEFAZ | Veja data.sefaz.codigo e data.sefaz.mensagem. Corrija e emita nova nota. |
Padrão de tratamento recomendado
class ApiError extends Error { constructor( public httpStatus: number, message: string, public payload: unknown, public requestId?: string ) { super(message); }}
async function call(method, path, body, token) { const res = await fetch(`${BASE}${path}`, { method, headers: { Authorization: `Token ${token}`, 'Content-Type': 'application/json', }, body: body ? JSON.stringify(body) : undefined, });
const json = await res.json().catch(() => ({}));
// Erro HTTP if (!res.ok) { const reqId = res.headers.get('x-request-id') ?? json?.meta?.request_id; throw new ApiError(res.status, json.error?.message ?? `HTTP ${res.status}`, json, reqId); }
// Erro de domínio if (json.status === '999') { throw new ApiError(200, json.data?.erro ?? 'Erro interno', json, json.meta?.request_id); }
return json;}Quando acionar o suporte
| Sintoma | Acionar suporte? | Como |
|---|---|---|
| Mensagem de validação clara | ❌ Corrija o payload. | — |
status: "050" | ❌ Use o recibo retornado. | — |
status: "900" com sefaz.mensagem claro | ❌ Corrija e emita de novo. | — |
status: "999" com mensagem genérica | ✅ Sim | Cite meta.request_id. |
HTTP 401 persistente após gerar Token novo | ✅ Sim | Cite X-Request-Id. |
Padrão repetitivo de 429 em volume normal | ✅ Sim | Cite janela e Token. |
Polling que nunca termina (002 por > 5 min) | ✅ Sim | Cite recibo e meta.request_id. |