Skip to content
WhatIsUp.dev

Erros

Todo corpo de resposta 4xx e 5xx usa o mesmo envelope:

{
  "error": {
    "code": "channel_unavailable",
    "message": "Channel is not connected. Re-pair to continue.",
    "details": { "channel_id": "..." }
  },
  "request_id": "req-3f7a1c02"
}

Faça match no code — ele é o contrato estável. message é legível por humanos e pode mudar entre releases. details é opcional e específico de cada código.

O conjunto completo de códigos vem no contrato Zod ERROR_CODES. A tabela abaixo é canônica.

Auth / identidade

CódigoQuando você vai ver
auth_missingNenhum header Authorization enviado.
auth_invalidO token bearer não corresponde a nenhuma chave ativa.
auth_malformedHeader presente mas não Bearer …, ou formato de token errado.
auth_expiredA chave ou o token passou da validade. (Revogação é auth_revoked.)
auth_no_emailO token de sessão do dashboard não tem email verificado.
auth_revokedA chave existia e foi revogada.
auth_backend_unavailable503, não 401. Seu token era válido; nossa busca de usuário falhou. Retente e mantenha a sessão — não deslogue o usuário.
email_not_verified403. Só no login do dashboard: primeiro acesso com um email que o provedor de login ainda não verificou. Abra o link de verificação e entre de novo. Chaves de API nunca recebem isso.
account_suspended403. A conta foi suspensa pela nossa equipe. Toda chamada com API key ou pelo dashboard é recusada até ela ser restaurada — retentar não adianta. A página da sua conta e os tickets de suporte continuam funcionando; fale com o suporte.

Ciclo de vida do canal

CódigoQuando você vai ver
channel_not_foundO UUID do canal não existe (ou pertence a outro cliente).
channel_access_deniedChave de API escopada por canal sendo usada contra um canal diferente.
insufficient_scope403. Os scopes da chave não cobrem esta rota. details traz required e granted. Um array scopes vazio é irrestrito e nunca dispara isso.
channel_unavailableO canal está em estado terminal (logged_out, stopped_by_user, disabled_by_admin, error, failed), ou nunca foi pareado (pending / qr com details.paired: false — escaneie o QR code antes).
idempotency_key_in_use409. Um envio com este Idempotency-Key ainda está rodando. Retente depois do Retry-After para receber o resultado.
idempotency_key_reused422. O Idempotency-Key já foi usado numa requisição diferente. Nada foi enviado — use uma chave nova por mensagem.
channel_scoped_keyA chave está escopada a um único canal, mas uma rota multi-canal foi chamada.
invalid_lifecycle_transitionTentou dar connect num canal já conectado, etc.
pair_code_unavailableO estado do canal não permite gerar um código de pareamento agora.
pair_code_failedO upstream rejeitou a requisição do código de pareamento.
no_qrQR ainda não pronto (o canal não inicializou), ou nenhum QR para este estado.
not_disabledTentou reativar um canal que não foi desativado por admin.
concurrent_modificationDuas escritas colidiram na mesma linha; refaça seu read-modify-write.

Plano / cota

CódigoQuando você vai ver
plan_channel_limitLimite de canais do plano atingido. Faça upgrade ou apague um canal existente.
rate_limitedUm token bucket esgotou. X-RateLimit-Scope diz qual (customer, channel ou ip; ip também protege os downloads públicos de mídia). Os buckets são por conta e por canal, não por chave, e compartilhados entre todos os nossos servidores. Respeite o Retry-After.
worker_at_capacityNenhum worker tem vaga livre agora — extremamente raro; refaça a retentativa.
trial_expired402. Teste acabou sem plano pago. Bloqueia toda ação que chega no WhatsApp — envios, stories, presença, grupos, comunidades, canais (newsletters), etiquetas, bloquear/desbloquear, recusar chamada, editar perfil, connect e pair-code — além da criação de canal; leituras, disconnect e logout continuam.
payment_overdue402. Conta em atraso. Mesmo bloqueio acima.
sse_connection_limit429. Mais de 10 streams /v1/events simultâneos na conta.

Proxy do canal

CódigoQuando você vai ver
proxy_invalid400. Não é uma URL de proxy utilizável — message diz qual parte (esquema, host, porta, credenciais).
proxy_blocked400. O host do proxy é um endereço privado, de loopback ou interno.
proxy_unreachable422. O teste pelo proxy falhou (details.check.error diz o motivo), ou o proxy salvo não pode ser lido. Nada foi salvo.
proxy_not_configured404. POST /v1/channels/:id/proxy/test sem url, num canal sem proxy salvo.
proxy_unavailable503. Não dá pra salvar proxies agora.
proxy_test_limit429. Mais de 10 testes de proxy em um minuto na sua conta. Respeite o Retry-After — nada mais é limitado.

Webhook + mídia

CódigoQuando você vai ver
webhook_endpoint_not_foundO UUID do endpoint não existe.
media_gone410. Token válido, mas os bytes passaram da retenção. Permanente — não adianta retentar.
media_token_expired401. O token de mídia assinado expirou.
media_token_tampered401. A assinatura do token de mídia não confere.
media_token_malformed401. O token de mídia não é parseável.
media_too_large413. A mídia passa do teto de 25 MB (pré-upload), ou de 64 MB no media_url de um envio.
media_fetch_failed400 no pré-upload: não conseguimos buscar seu source_url (DNS, não-2xx, ou timeout). 422 num envio, status ou foto: seu media_url respondeu 4xx, redirecionou mais de 3 vezes ou veio vazio. Timeout ou 5xx num envio vai pra fila e é tentado de novo.
media_fetch_blocked400. source_url / media_url resolve para endereço privado / loopback / link-local / interno — checado em cada redirecionamento.
message_not_found404. Nenhuma mensagem com esse id nesse canal.
chat_not_found404. Nenhum chat assim nesse canal.
contact_not_found404. Nenhum contato assim nesse canal.

Genéricos

CódigoQuando você vai ver
invalid_requestCorpo ou query falhou na validação Zod. details contém a lista de problemas.
not_found404 genérico.
bad_request400 genérico onde nenhum código mais específico se encaixa.
no_opA mutação não teve efeito (já estava no estado-alvo).
otherCatch-all para erros do provedor upstream.
internal_errorExceção não tratada no servidor. Somos alertados; request_id é a chave de rastreamento.
undeliverable_recipient400. to é um @lid, tem mais de 15 dígitos, não é numérico, tem sufixo desconhecido, ou o WhatsApp diz que o número não está registrado. details.to carrega o valor.
not_implemented501. O endpoint existe mas é um stub declarado (GET /v1/channels/:id/stories, GET /v1/channels/:id/calls).
payload_too_large413. O corpo da requisição passou do limite do servidor.

Esta lista não é fechada. Compare por code sempre com um ramo default — códigos novos entram sem bump de versão maior, e algumas rotas emitem códigos fora do conjunto central documentado aqui.

Boas práticas

  • Sempre registre o request_id. O suporte ao cliente usa ele para achar a requisição exata nos nossos traces.
  • Faça match no code, nunca na message. As mensagens são afinadas para legibilidade humana e mudam com o tempo.
  • Trate códigos desconhecidos como transitórios. Novos códigos são adicionados; assuma que dá pra retentar até a documentação ser atualizada.
  • Para 429, respeite o Retry-After em vez de chutar o backoff.