Erros e Rejeições
Referência completa de erros HTTP e rejeições SEFAZ. Causas, soluções e estratégia de retry.
Erros e Rejeições
A Engine API segue o padrão HTTP para códigos de status e acrescenta campos específicos para rejeições da SEFAZ.
Formato de Erro
{
"statusCode": 422,
"error": "Unprocessable Entity",
"message": "Nota rejeitada pela SEFAZ",
"sefazCode": 539,
"sefazMessage": "Duplicidade de NFe, com diferença na Chave de Acesso"
}
| Campo | Presente em | Descrição |
|-------|------------|-----------|
| statusCode | Todos os erros | Código HTTP |
| error | Todos os erros | Descrição HTTP do status |
| message | Todos os erros | Mensagem legível |
| sefazCode | Somente 422 | Código de rejeição SEFAZ |
| sefazMessage | Somente 422 | Mensagem oficial da SEFAZ |
Códigos HTTP
| Código | Significado | Quando ocorre |
|--------|-------------|--------------|
| 200 | Sucesso | Requisição processada com sucesso |
| 400 | Bad Request | JSON inválido, campos faltando ou com formato errado |
| 401 | Unauthorized | Token ausente, inválido ou expirado |
| 403 | Forbidden | Sem permissão ou assinatura sem plano ativo |
| 404 | Not Found | Recurso (nota, empresa, webhook) não encontrado |
| 409 | Conflict | Recurso já existe (ex: certificado duplicado) |
| 422 | Unprocessable Entity | SEFAZ rejeitou o documento |
| 429 | Too Many Requests | Rate limit do plano excedido |
| 500 | Internal Server Error | Erro interno. Contate o suporte |
| 503 | Service Unavailable | SEFAZ temporariamente indisponível |
Rejeições SEFAZ (422): Top 20
Estratégia de Retry
Nem todo erro deve ser retentado. Siga esta lógica:
| Tipo de erro | Retry? | Quando |
|-------------|--------|--------|
| 400 Bad Request | Não Não | Corrija os dados primeiro |
| 401 Unauthorized | Não Não | Faça login e obtenha novo token |
| 403 Forbidden | Não Não | Verifique permissões do plano |
| 404 Not Found | Não Não | Recurso não existe |
| 422 Rejeição SEFAZ | Não Não | Corrija conforme sefazCode |
| 429 Rate Limit | Sim Sim | Aguarde e tente novamente |
| 500 Server Error | Sim Sim | Retry com backoff exponencial |
| 503 SEFAZ indisponível | Sim Sim | Retry após 5 minutos |
Implementando Retry com Backoff Exponencial
async function emitirComRetry(data: any, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const resp = await fetch('https://api.engineapi.com.br/nfe/emitir', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (resp.ok) return await resp.json();
const error = await resp.json();
// Não fazer retry em erros de dados
if ([400, 401, 403, 404, 422].includes(resp.status)) {
throw new Error(`Erro não recuperável: ${error.message}`);
}
// Retry em 500 e 503
if (attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
} catch (err) {
if (attempt === maxRetries - 1) throw err;
}
}
}
Rate Limits
| Plano | Requests/min | Requests/dia | |-------|-------------|-------------| | Dev | 5 | 100 | | Starter | 60 | 1.000 | | Growth | 300 | 10.000 | | Scale | 600 | Ilimitado | | Enterprise | Ilimitado | Ilimitado |
Quando o limite é excedido:
{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Rate limit excedido. Seu plano permite 60 requests/min.",
"retryAfter": 45
}
Use o campo retryAfter (em segundos) para saber quando tentar novamente.