Códigos de erro
Erros gerados pelo gateway usam o mesmo envelope:
{
"error": {
"code": "document_required",
"message": "A J&T Express exige o documento do destinatário no header X-Tracking-Document.",
"param": "X-Tracking-Document",
"courier": "jtexpress",
"doc_url": "{PUBLIC_APP_URL}/?docs=errors#document_required",
"request_id": "0ebcea5b-1727-4bf9-af88-4deb11b438f2"
}
}
| Campo | Sempre presente | Descrição |
|---|---|---|
code |
Sim | Identificador estável. É nele que sua aplicação deve decidir. |
message |
Sim | Texto legível em português. Pode ser reescrito a qualquer momento — não compare esta string. |
request_id |
Sim | Igual ao header X-Request-Id. Cite ao abrir um chamado. |
param |
Não | Parâmetro, segmento de rota ou header que causou o erro |
courier |
Não | Transportadora envolvida, quando houver |
doc_url |
Não | Link direto para a seção correspondente no portal público |
Nas rotas normalizadas, erros não chegam com status 2xx. Nas rotas /raw, o
payload original pode conter um erro da transportadora mesmo com HTTP 200;
nesse caso ele não usa este envelope. Verifique o formato da transportadora
antes de tratar res.ok como rastreamento encontrado. Veja
Resposta crua.
Lista completa
| Status | code |
Quando acontece | Consome cota |
|---|---|---|---|
400 |
invalid_request |
Requisição malformada que nenhum outro código descreve, incluindo caminho acima de 1.024 caracteres | Não |
400 |
invalid_tracking_code |
Código informado acima de 40 caracteres, com caracteres inválidos (inclusive espaços) ou fora do formato aceito pela transportadora | Não |
400 |
invalid_retry_level |
retry_level fora de low, medium, high |
Não |
400 |
courier_not_supported |
Slug de transportadora fora do catálogo | Não |
401 |
missing_api_key |
Header X-API-Key ausente |
Não |
401 |
invalid_api_key |
Chave desconhecida ou revogada | Não |
403 |
subscription_inactive |
Sem assinatura ativa | Não |
403 |
account_suspended |
A conta foi suspensa. Entre em contato com o suporte | Não |
403 |
phone_verification_required |
Plano Básico sem telefone verificado | Não |
404 |
not_found |
O caminho não é um endpoint da API, inclusive /v1/track/ sem o segmento do código |
Não |
404 |
tracking_not_found |
A transportadora foi consultada e não tem registro do código | Sim |
405 |
method_not_allowed |
O endpoint existe, mas aceita apenas GET |
Não |
413 |
payload_too_large |
Corpo acima de 1 MiB (1.048.576 bytes) ou sem tamanho confiável | Não |
422 |
courier_not_detected |
Código bem formado que não corresponde a nenhuma transportadora suportada | Não |
422 |
document_required |
Requisição J&T Express sem X-Tracking-Document |
Não |
422 |
invalid_document |
Reservado para quando a transportadora identificar que recusou o documento | Sim |
429 |
rate_limit_exceeded |
Limite por minuto atingido. Traz Retry-After |
Não |
429 |
quota_exceeded |
Cota do período esgotada | Não |
500 |
internal_error |
Falha nossa | Não |
502 |
courier_unavailable |
Transportadora indisponível ou com resposta ininterpretável | Não |
503 |
service_unavailable |
Nosso serviço degradado. Traz Retry-After |
Não |
504 |
courier_timeout |
O orçamento de tempo do retry_level se esgotou |
Não |
Detalhes de cobrança em Cobrança de requisições.
Como interpretar cada família
4xx— algo na requisição precisa mudar. Repetir sem alteração produz o mesmo erro. As exceções são429(espere) e404 tracking_not_found(o código pode passar a existir mais tarde).5xx— o problema é nosso ou da transportadora, e nenhuma delas é cobrada. Repetir mais tarde é razoável.
Tratamento recomendado para rotas normalizadas
const res = await fetch(url, { headers: { "X-API-Key": key } });
if (res.ok) {
return res.json();
}
const { error } = await res.json();
switch (error.code) {
case "tracking_not_found":
return null; // sem informação para este código
case "courier_not_detected":
throw new Error("Informe a transportadora na rota para este código.");
case "document_required":
throw new Error("Reenvie com o header X-Tracking-Document.");
case "rate_limit_exceeded": {
const wait = Number(res.headers.get("Retry-After") ?? 1);
await sleep(wait * 1000);
return retry();
}
case "quota_exceeded":
throw new Error("Cota do período esgotada — faça upgrade do plano.");
case "account_suspended":
// Repetir não resolve: só a remoção da suspensão restabelece o acesso.
throw new Error("Conta suspensa — entre em contato com o suporte.");
case "courier_unavailable":
case "courier_timeout":
case "service_unavailable":
return retryLater(); // falha temporária, não é cobrada
default:
throw new Error(`${error.code}: ${error.message} (${error.request_id})`);
}
Compatibilidade
Novos code podem ser adicionados sem aviso. Sempre tenha um caminho
default que trate pelo status HTTP — um code desconhecido nunca deve
derrubar sua integração.