Errors
A API usa um formato uniforme de erro para todas as respostas não-2xx.
Formato
{
"code": "PHONE_NOT_FOUND",
"message": "Phone not found",
"details": { "phoneId": "5f7b2e1c-..." }
}
| Campo | Tipo | Descrição |
|---|---|---|
code | string | Identificador estável da causa (use no seu código) |
message | string | Descrição human-readable (em inglês ou português; não dependa do conteúdo) |
details | object | undefined | Contexto adicional (varia por erro) |
HTTP status code segue convenções REST. Você pode confiar no code antes do message.
Erros de validação Zod retornam:
{
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "path": ["webhook", "secret"], "message": "String must contain at least 16 character(s)" }
]
}
Catálogo completo
4xx — erros do cliente
400 Bad Request
| Code | Origem | Descrição |
|---|---|---|
VALIDATION_ERROR | Zod | Body/query/params não passou no schema |
WABA_CREDENTIALS_REQUIRED | POST /v1/phones | type=WABA sem credentials |
WABA_CREDS_INCOMPLETE | Templates | Phone WABA sem wabaPhoneNumberId/accessToken/businessAccountId |
WABA_CREDS_MISSING | Templates | Token decryptado vazio |
WABA_TOKEN_MISSING | Vários | Token WABA não decifrável |
QR_NOT_APPLICABLE | GET /:id/qrcode | Phone não é BAILEYS |
PAIRING_NOT_APPLICABLE | POST /:id/pairing-code | Phone não é BAILEYS |
LOOKUP_UNSUPPORTED_CHANNEL | POST /v1/lookup | Phone não é BAILEYS |
MEDIA_NOT_AUDIO | POST /:id/transcribe | contentType não é audio/* |
MEDIA_NOT_IMAGE | POST /:id/ocr | contentType não é image/* |
MISSING_PHONE_ID | Media upload | Header X-Phone-Id ausente |
RECORDING_EMPTY | POST /v1/calls/:id/recording | Body não é Buffer ou tamanho 0 |
INVALID_TO | Send | to malformado |
INVALID_JID | Baileys send | JID inválido após normalização |
INVALID_TOKEN | POST /:id/rotate-token | Novo token não passou na validação Meta |
UNSUPPORTED_TYPE | Send | type não suportado no canal |
EMPTY_BATCH | Lookup/Block | Array vazio |
BATCH_TOO_LARGE | Lookup/Block | >100 itens |
SCHEDULE_TOO_SOON | POST /v1/messages/scheduled | scheduledAt <agora + 5s |
FLOW_NOT_SYNCED | Flows | Flow ainda não tem metaFlowId |
401 Unauthorized
| Code | Quando |
|---|---|
AUTH_MISSING | Sem X-API-Key em endpoint protegido |
AUTH_INVALID | Key inexistente |
AUTH_REVOKED | Key revogada via POST /v1/admin/api-keys/:id/revoke |
AUTH_EXPIRED | expiresAt passou |
402 Payment Required
| Code | Quando |
|---|---|
ACCOUNT_SUSPENDED | Account em dunning SUSPENDED (14+ dias atraso). Cliente precisa pagar (/v1/billing/invoices/:id/issue) ou contatar admin para reativar |
403 Forbidden
| Code | Quando |
|---|---|
ACCOUNT_INACTIVE | Account CANCELLED ou outro status ≠ ACTIVE |
ADMIN_FORBIDDEN | Endpoint /v1/admin/* sem X-Admin-Token válido |
404 Not Found
| Code | Quando |
|---|---|
PHONE_NOT_FOUND | Phone inexistente, não é WABA quando exigido, ou de outra account |
MESSAGE_NOT_FOUND | Message UUID local inexistente |
MESSAGE_KEY_NOT_FOUND | whatsappMessageId fora do cache (TTL 7d) — operações react/edit/delete impossíveis |
MEDIA_NOT_FOUND | Media UUID inexistente |
TEMPLATE_NOT_FOUND | Template inexistente |
SCHEDULED_NOT_FOUND | Scheduled message inexistente |
IDENTITY_NOT_FOUND | Key não resolveu para nenhuma identidade no phone |
TASK_NOT_FOUND | Lookup task inexistente ou expirada |
AGENT_NOT_FOUND | Phone sem AI Agent configurado |
DOC_NOT_FOUND | Knowledge doc inexistente |
CONVERSATION_NOT_FOUND | Agent conversation inexistente |
FLOW_NOT_FOUND | Flow inexistente |
GROUP_NOT_FOUND | WABA Group inexistente |
CALL_NOT_FOUND | Call inexistente |
INVOICE_NOT_FOUND | Invoice inexistente |
DELIVERY_NOT_FOUND | Webhook delivery inexistente |
RECORDING_NOT_FOUND | Call sem recording upload |
TRANSCRIPTION_NOT_REQUESTED | Você não chamou POST /transcribe antes do GET |
OCR_NOT_REQUESTED | Você não chamou POST /ocr antes do GET |
ACCOUNT_NOT_FOUND | Admin: account inexistente |
PLAN_NOT_FOUND | Admin: billing plan code inexistente |
NO_PUBLIC_KEY | Flow sem keypair gerado (não é data_exchange) |
409 Conflict
| Code | Quando |
|---|---|
ALREADY_CONNECTED | GET /qrcode ou POST /pairing-code em phone já CONNECTED |
ALREADY_DELIVERED | POST /v1/events/:id/replay em delivery já entregue |
PHONE_NOT_CONNECTED | Send/op em phone não CONNECTED |
CONVERSATION_CLOSED | Tentou release em conversation CLOSED |
SCHEDULED_NOT_PENDING | PATCH/DELETE em scheduled com status ≠ PENDING |
TEMPLATE_NOT_SUBMITTED | PATCH /templates/:id antes do metaTemplateId |
GROUP_SUSPENDED | Operação em group suspended=true |
410 Gone
| Code | Quando |
|---|---|
MEDIA_EXPIRED | TTL 7d passou |
MEDIA_DELETED | ACK explícito já fez delete |
425 Too Early
| Code | Quando |
|---|---|
QR_NOT_READY | QR ainda não emitido pelo Worker — tente em 1-2s |
PAIRING_NOT_READY | Pairing code ainda não gerado |
429 Too Many Requests
| Code | Quando |
|---|---|
RATE_LIMITED | Excedeu 600 req/min (300 em /v1/messages). Header Retry-After indica espera |
SCHEDULE_QUOTA_EXCEEDED | >10.000 scheduled messages PENDING |
5xx — erros do servidor
500 Internal Server Error
| Code | Quando |
|---|---|
INTERNAL_ERROR | Catch-all para exceções não mapeadas (não exposto detalhe) |
BAILEYS_NO_ID | Baileys não retornou ID na mensagem enviada |
FORM_DATA_MISSING | Upload multipart corrompido |
IG_TOKEN_BAD | Token Instagram falhou validação |
KEYPAIR_INVALID | Geração de keypair RSA falhou |
META_NO_ID / META_NO_CALL_ID | Meta retornou OK sem ID na resposta |
OPENAI_KEY_DECRYPT_FAILED | Key OpenAI armazenada não decifra |
WABA_TOKEN_INVALID | Token armazenado falhou decrypt |
502 Bad Gateway
| Code | Quando |
|---|---|
AI_BAD_OUTPUT | OpenAI retornou JSON inválido em endpoint que espera JSON estruturado |
503 Service Unavailable
| Code | Quando |
|---|---|
ADMIN_DISABLED | Servidor sem ADMIN_TOKEN configurado |
ASAAS_NOT_CONFIGURED | Servidor sem ASAAS_API_KEY |
IG_NOT_CONFIGURED | Servidor sem credenciais Instagram |
META_APP_ID_MISSING | GET /waba/embedded-signup/config sem META_APP_ID |
META_APP_NOT_CONFIGURED | POST /waba/embedded-signup sem META_APP_ID/META_APP_SECRET |
Erros transientes vs definitivos
Retry com backoff para:
503/502/500425 QR_NOT_READY,425 PAIRING_NOT_READY429 RATE_LIMITED(respeitandoRetry-After)
NÃO retry para:
4xxexceto os acima (corrige a request)402 ACCOUNT_SUSPENDED(até pagar)404/409/410(semânticos, não vão mudar com retry)
Códigos Meta repassados
Algumas operações WABA propagam códigos Meta diretamente no message ou details. Os mais comuns:
| Meta code | Significado |
|---|---|
131026 | Recipient not on WhatsApp |
131047 | Re-engagement message — 24h window fechada, precisa template |
131051 | Mensagem fora da janela 24h |
131057 | Account spamming / restricted |
132000-132099 | Template-related (rejected, paused, not approved) |
133010 | Phone number not registered |
133015 | Phone re-registration required |
190 | Access token expirado |
4 / 17 / 32 | Rate limit Meta (META_RATE_LIMIT em logs) |
Quando estes aparecem no failedReason de messages_sent, ação típica:
- Token expirado →
POST /v1/phones/:id/rotate-token - Janela 24h → use template
- Recipient not on WhatsApp →
POST /v1/lookupantes (Baileys-only) ou aceite custo de FAILED
Códigos Instagram
| IG code | Significado |
|---|---|
4 / 17 | Rate limit (IG_RATE_LIMIT) |
190 | Token expirado — re-OAuth |
100 | Parâmetro inválido (ex.: IGSID inexistente) |
Boas práticas
- Code é estável; message não. Faça switch em
code, nunca emmessage. - Sempre logue
code+detailsquando capturar — facilita troubleshooting. - Implemente fallback para
INTERNAL_ERROR— pode ser bug do gateway. Reporte comX-Request-Id(response header). - Diferencie 402 de 4xx.
ACCOUNT_SUSPENDEDexige fluxo de cobrança UX, não pode ser tratado como "input ruim".