19. Códigos de Erro#
Formato unificado de resposta#
Todos os erros seguem o mesmo envelope JSON, com error_code estável para handling tipado e trace_id para casamento exato com o log do backend:
{
"error_code": "INSTANCE_NOT_CONNECTED",
"message": "instance is not connected (status: DISCONNECTED)",
"trace_id": "2a62142c-f55f-4db4-8c7b-060018e3ef48",
"error": "instance is not connected (status: DISCONNECTED)"
}
| Campo | Descricao |
|---|---|
error_code |
Código estável em SCREAMING_SNAKE_CASE. Use isso pra lógica condicional no cliente. |
message |
Mensagem legivel para humanos. |
trace_id |
UUID que identifica unicamente o request. Também disponível no header de resposta X-Trace-ID. Use para correlacionar com GET /v1/admin/logs?trace_id=<uuid>. |
error |
Campo legacy, identico a message. Mantido por uma release para integradores antigos. Migre para message. |
O header X-Trace-ID está exposto via CORS e pode ser lido pelo navegador (error.response.headers['x-trace-id']).
Códigos HTTP#
| Código | error_code padrão | Descricao |
|---|---|---|
400 |
BAD_REQUEST |
Corpo inválido, campos faltando ou inválidos |
401 |
UNAUTHORIZED / TOKEN_INVALID |
Token ausente, expirado ou inválido |
403 |
FORBIDDEN / COMPANY_SUSPENDED / INSUFFICIENT_PERMISSIONS |
Sem permissão, empresa suspensa |
404 |
NOT_FOUND / INSTANCE_NOT_FOUND / MESSAGE_NOT_FOUND / WEBHOOK_NOT_FOUND |
Recurso não encontrado |
409 |
CONFLICT / DUPLICATE_INSTANCE_NAME / INSTANCE_NOT_CONNECTED |
Estado conflitante |
422 |
UNPROCESSABLE_ENTITY |
Dados válidos mas não processaveis |
429 |
RATE_LIMITED / ACCOUNT_LOCKED |
Rate limit excedido. Verifique os headers Retry-After e X-RateLimit-* |
500 |
INTERNAL_ERROR |
Erro interno do servidor |
503 |
SERVICE_UNAVAILABLE |
Serviço degradado (MySQL/Redis/S3, lookup de empresa no JWT) |
Códigos de erro estáveis#
A lista canonica vive em internal/api/error_codes.go. Os principais:
error_code |
Quando ocorre |
|---|---|
| Auth | |
INVALID_CREDENTIALS |
Email ou senha errados no login |
ACCOUNT_LOCKED |
Mais de 5 tentativas falhas em 15min |
TOKEN_INVALID |
JWT malformado, expirado ou não encontrado |
PASSWORD_CHANGED |
JWT foi emitido antes da senha ser alterada (ex: reset via link). O refresh-token também já foi revogado — cliente deve descartar o token e ir direto pro /login (ver PASSWORD_RESET_TTL) |
REFRESH_FAILED |
Refresh token ausente ou já consumido |
RESET_TOKEN_INVALID |
Token de reset não encontrado ou já usado (link expirou ou já consumido) |
RESET_TOKEN_EXPIRED |
Token de reset passou da janela de 30min (PASSWORD_RESET_TTL) |
EMAIL_ALREADY_REGISTERED |
Email já em uso no register |
REGISTRATION_FAILED |
Falha interna durante o cadastro |
CSRF_INVALID |
Token CSRF ausente ou não bate em request unsafe |
OAUTH_PROVIDER_INVALID |
Provider social desconhecido |
OAUTH_PROVIDER_DISABLED |
Provider social desabilitado ou sem credenciais configuradas |
OAUTH_STATE_INVALID |
State OAuth ausente, expirado ou inválido |
OAUTH_EMAIL_UNVERIFIED |
Provider não confirmou email verificado |
OAUTH_PENDING_INVALID |
Token de conclusao de cadastro social inválido ou expirado |
OAUTH_UPSTREAM_FAILED |
Falha ao conversar com Google/GitHub |
| Tenancy | |
COMPANY_SUSPENDED |
Empresa está com status != active |
PLAN_LIMIT_REACHED |
Limite do plano atingido |
| Instância | |
INSTANCE_NOT_FOUND |
Instância não existe ou não pertence a empresa |
INSTANCE_NOT_CONNECTED |
Tentativa de send em instância que não está CONNECTED |
INSTANCE_BANNED |
Instância banida pelo WhatsApp |
INSTANCE_HARD_PAUSED |
Throttler em hard pause após rate limits repetidos |
DUPLICATE_INSTANCE_NAME |
Nome de instância já em uso na empresa |
INVALID_INSTANCE_TRANSITION |
Transicao inválida (ex: connect em CREATED) |
| Mensageria | |
INVALID_JSON |
Corpo não e JSON válido |
MISSING_FIELD |
Campo obrigatório ausente |
INVALID_RECIPIENT |
Formato de destinatário inválido |
INVALID_PHONE |
Número brasileiro com formato errado |
IDEMPOTENCY_KEY_REQUIRED |
Header Idempotency-Key ausente em endpoint async |
MEDIA_TOO_LARGE |
Arquivo excede limite de upload |
UNSUPPORTED_MEDIA_TYPE |
MIME type não aceito |
MEDIA_FETCH_FAILED |
API tentou baixar media_url no momento do request e falhou (DNS, 4xx/5xx do origem, SSRF block, timeout, oversized). HTTP 502. NÃO houve retry — request inteiro falha sincronamente, nada vai pra fila. Retry com URL válida ou faca upload prévio em /media. |
MESSAGE_NOT_FOUND |
Mensagem não existe na tenant DB |
INVALID_PIX_KEY |
Chave PIX inválida para o tipo declarado (CPF/CNPJ/PHONE/EMAIL/EVP). HTTP 400 |
INTERACTIVE_DISABLED |
Endpoints de mensagem interativa (button/PIX/OTP/option-list) estão desativados — destinatários não renderizam via multi-device. HTTP 403. Ver API-MANUAL seção de mensagens + comparativo §15.9 |
SEND_FAILED |
Falha ao criar/enfileirar a task |
BLOCKED_DUPLICATE_CONTENT |
Anti-spam guard bloqueou conteúdo identico já enviado em 24h. Bypass: header X-Force-Send: true. |
BLOCKED_NO_RECIPROCITY |
Anti-spam guard bloqueou envio consecutivo sem inbound do contato. Bypass: header X-Force-Send: true. |
BLOCKED_TEMPERATURE_LIMIT |
Regra de temperatura bloqueou outbound — max_consecutive excedido para o tier do score do contato (cold=2, warm=3, engaged=4, hot=5, very_hot=7). Payload inclui score, tier, max_consecutive, outbound_consecutive. Bypass: header X-Force-Send: true (audit-logged). |
| Webhook | |
WEBHOOK_NOT_FOUND |
Webhook não existe |
WEBHOOK_URL_INVALID |
URL inválida (HTTP em prod, scheme não suportado, SSRF) |
WEBHOOK_URL_BLOCKED |
URL em dominio bloqueado |
Casando o erro do frontend com o log do backend#
Quando o frontend recebe um erro, ele também recebe o trace_id. Para encontrar a linha exata no log:
TRACE_ID="2a62142c-f55f-4db4-8c7b-060018e3ef48"
TOKEN=$(curl -sf https://api.catcher.one/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@biazap.test","password":"<senha>"}' | jq -r .token)
curl -sf "https://api.catcher.one/v1/admin/logs?trace_id=$TRACE_ID&limit=20" \
-H "Authorization: Bearer $TOKEN" | jq '.lines[]' -r
O endpoint /v1/admin/logs aceita os filtros: file, limit, skip, level, contains, instance_id, trace_id, error_code. Use trace_id para uma única request, error_code para encontrar todas as ocorrências de um erro especifico.
A linha de log do request completed inclui os campos trace_id, error_code, status, duration_ms, path, method, remote_addr — tudo o que você precisa para diagnosticar o problema.