Códigos de Erro

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:

json
{
  "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:

bash
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.