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ódigo | Quando você vai ver |
|---|---|
auth_missing | Nenhum header Authorization enviado. |
auth_invalid | O token bearer não corresponde a nenhuma chave ativa. |
auth_malformed | Header presente mas não Bearer …, ou formato de token errado. |
auth_expired | A chave ou o token passou da validade. (Revogação é auth_revoked.) |
auth_no_email | O token de sessão do dashboard não tem email verificado. |
auth_revoked | A chave existia e foi revogada. |
auth_backend_unavailable | 503, 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. |
Ciclo de vida do canal
| Código | Quando você vai ver |
|---|---|
channel_not_found | O UUID do canal não existe (ou pertence a outro cliente). |
channel_access_denied | Chave de API escopada por canal sendo usada contra um canal diferente. |
insufficient_scope | 403. Os scopes da chave não cobrem esta rota. details traz required e granted. Um array scopes vazio é irrestrito e nunca dispara isso. |
channel_unavailable | O canal está em estado terminal (logged_out, stopped_by_user, disabled_by_admin, error, failed). |
channel_scoped_key | A chave está escopada a um único canal, mas uma rota multi-canal foi chamada. |
invalid_lifecycle_transition | Tentou dar connect num canal já conectado, etc. |
pair_code_unavailable | O estado do canal não permite gerar um código de pareamento agora. |
pair_code_failed | O upstream rejeitou a requisição do código de pareamento. |
no_qr | QR ainda não pronto (o canal não inicializou), ou nenhum QR para este estado. |
not_disabled | Tentou reativar um canal que não foi desativado por admin. |
concurrent_modification | Duas escritas colidiram na mesma linha; refaça seu read-modify-write. |
Plano / cota
| Código | Quando você vai ver |
|---|---|
plan_channel_limit | Limite de canais do plano atingido. Faça upgrade ou apague um canal existente. |
rate_limited | Um token bucket esgotou. X-RateLimit-Scope diz qual (customer, channel ou ip). Os buckets são por conta e por canal, não por chave. Respeite o Retry-After. |
worker_at_capacity | Nenhum worker tem vaga livre agora — extremamente raro; refaça a retentativa. |
trial_expired | 402. Teste acabou sem plano pago. Bloqueia envios, stories, presença e criação de canal; leituras continuam. |
payment_overdue | 402. Conta em atraso. Mesmo bloqueio acima. |
sse_connection_limit | 429. Mais de 10 streams /v1/events simultâneos na conta. |
Webhook + mídia
| Código | Quando você vai ver |
|---|---|
webhook_endpoint_not_found | O UUID do endpoint não existe. |
media_gone | 410. Token válido, mas os bytes passaram da retenção. Permanente — não adianta retentar. |
media_token_expired | 401. O token de mídia assinado expirou. |
media_token_tampered | 401. A assinatura do token de mídia não confere. |
media_token_malformed | 401. O token de mídia não é parseável. |
media_too_large | 413. A mídia passa do teto de 25 MB. |
media_fetch_failed | 400. Não conseguimos buscar seu source_url (DNS, não-2xx, ou timeout). |
media_fetch_blocked | 400. source_url resolve para endereço privado / loopback / link-local. |
message_not_found | 404. Nenhuma mensagem com esse id nesse canal. |
chat_not_found | 404. Nenhum chat assim nesse canal. |
contact_not_found | 404. Nenhum contato assim nesse canal. |
Genéricos
| Código | Quando você vai ver |
|---|---|
invalid_request | Corpo ou query falhou na validação Zod. details contém a lista de problemas. |
not_found | 404 genérico. |
bad_request | 400 genérico onde nenhum código mais específico se encaixa. |
no_op | A mutação não teve efeito (já estava no estado-alvo). |
other | Catch-all para erros do provedor upstream. |
internal_error | Exceção não tratada no servidor. Somos alertados; request_id é a chave de rastreamento. |
undeliverable_recipient | 400. 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_implemented | 501. O endpoint existe mas é um stub declarado (GET /v1/channels/:id/stories, GET /v1/channels/:id/calls). |
payload_too_large | 413. 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 namessage. 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-Afterem vez de chutar o backoff.