Pular para o conteúdo principal

Erros

Toda falha gerada pelo Joinotify usa o mesmo envelope:

{
"error": {
"message": "Frase legível descrevendo o que aconteceu.",
"type": "codigo_estavel"
}
}

Programe contra o campo type — ele é estável. O message é escrito para humanos e pode mudar de redação sem aviso.

Campos opcionais​

Alguns campos aparecem quando fazem sentido.

issues — falhas de validação​

422 Unprocessable Entity
{
"error": {
"message": "Invalid request payload.",
"type": "invalid_request",
"issues": [
{
"code": "too_small",
"path": ["body"],
"message": "String must contain at least 1 character(s)"
}
]
}
}

meta — erro repassado da Meta​

Quando a recusa vem da Meta, devolvemos o corpo original dela intacto. É onde estão o code, o error_subcode e o fbtrace_id que o suporte da Meta pede.

422 Unprocessable Entity
{
"error": {
"message": "The Meta Cloud API rejected the request.",
"type": "meta_error",
"meta": {
"error": {
"message": "(#131047) Re-engagement message",
"type": "OAuthException",
"code": 131047,
"error_subcode": 2494010,
"fbtrace_id": "AbCdEfGhIjK"
}
}
}
}

reason e action — sem direito de uso​

Um 402 diz por que o acesso foi bloqueado e qual é a saída: o que fazer (action.kind) e a página do painel onde se faz (action.url). Mostre as duas coisas a quem administra a integração — num log de erro, "pagamento necessário" sem um link é um beco sem saída.

402 Payment Required
{
"error": {
"message": "Your free trial has ended. Subscribe to keep using the API.",
"type": "payment_required",
"reason": "trial_ended",
"billable_numbers": 1,
"action": {
"kind": "subscribe",
"url": "https://app.joinotify.com/checkout"
}
}
}
action.kindQuando
subscribeTeste encerrado, assinatura cancelada ou inexistente
update_payment_methodO último pagamento falhou (past_due)
pay_invoicesHá faturas em aberto (unpaid)
finish_checkoutO primeiro pagamento ainda não foi confirmado (incomplete)
resume_subscriptionA assinatura está pausada (paused)
renew_licenseA licença expirou (license_expired)
upgrade_planO teto mensal de mensagens do plano foi atingido (message_limit)
manage_numbersHá mais números conectados do que a conta paga (capacity_exceeded)

O bloqueio não chega sem aviso. Durante a carência as requisições continuam passando, e a resposta traz o header X-Joinotify-Billing-Warning com o motivo — past_due, por exemplo. Registre esse header nos seus logs: é ele que avisa que o 402 está a caminho.

allowed — método não aceito​

Um caminho que existe, chamado com o método errado, responde 405, e não 404. Os métodos aceitos vêm no header Allow e em error.allowed, para você procurar o erro no verbo e não na URL.

405 Method Not Allowed
{
"error": {
"message": "PUT is not supported on this route. Allowed: DELETE, GET, HEAD.",
"type": "method_not_allowed",
"allowed": ["DELETE", "GET", "HEAD"]
}
}

request_id — falha inesperada​

Um 500 não diz o que quebrou — a causa fica nos nossos registros, nunca na resposta. O que ele traz é o request_id, o mesmo valor do header x-request-id. Mande esse id ao suporte e nós achamos a falha.

500 Internal Server Error
{
"error": {
"message": "Internal server error.",
"type": "internal",
"request_id": "0d72872e-f70e-4c25-95c6-6bec4d33a800"
}
}

Tabela de códigos​

HTTPtypeSignificadoO que fazer
400invalid_requestNenhum ID de recurso depois de /v1/.Corrija o path.
400invalid_requestO corpo não é JSON válido, ou o path tem uma sequência % malformada.Corrija o corpo ou a codificação da URL.
401authenticationToken ausente, malformado, desconhecido ou revogado.Confira o header e a chave.
402payment_requiredSem direito de uso: teste encerrado, pagamento pendente, teto do plano…Siga error.action.
403tenant_inactiveConta suspensa ou cancelada.Fale com o suporte.
403forbiddenO recurso não pertence à sua conta.Confirme o phone_number_id / waba_id.
403forbidden_endpointCaminho na lista de bloqueio do proxy.Veja Compatibilidade com a Meta.
404not_foundRota, chave, endpoint ou entrega inexistente.Confira o ID.
405method_not_allowedO caminho existe, mas não com esse método.Use um dos métodos de Allow.
409no_phone_numberNenhum número conectado e from omitido.Conecte um número ou informe from.
409no_wabaNenhuma conta empresarial conectada e wabaId omitido.Conecte uma WABA ou informe wabaId.
409idempotency_in_progressUma requisição com o mesmo Idempotency-Key ainda está rodando.Aguarde e repita com a mesma chave.
422invalid_requestPayload reprovado na validação.Leia issues.
413payload_too_largeO corpo é maior do que a rota aceita.Divida o lote ou reduza o corpo.
415unsupported_media_typeCharset ou Content-Encoding do corpo não aceito.Envie JSON em UTF-8, sem compressão ou com gzip.
422meta_errorA Meta recusou (4xx).Leia error.meta.
422idempotency_key_reusedO Idempotency-Key já foi usado com outro corpo.Use uma chave nova por operação.
429rate_limitLimite da categoria estourado.Aguarde Retry-After. Veja Limites de uso.
500internalFalha inesperada do nosso lado.Tente de novo; se persistir, envie o request_id.
502meta_errorA Meta respondeu com erro 5xx.Tente de novo com backoff.
502upstream_errorNão conseguimos alcançar a Meta.Tente de novo com backoff.
504upstream_errorA Meta demorou mais de 30 segundos.Tente de novo com backoff.

Erro nosso ou erro da Meta?​

Uma regra simples: se existe o campo error.meta, a recusa foi da Meta. O Joinotify apenas repassou.

Isso importa na hora de agir. invalid_request significa que o seu payload está errado e uma nova tentativa idêntica vai falhar de novo. Já meta_error costuma ser regra de negócio do WhatsApp — janela de 24 horas fechada, template não aprovado, número inválido — e a correção é outra.

No espelho /v1/* a regra muda: quando a Meta responde, você recebe a resposta dela, no formato dela, sem o nosso envelope. O envelope do Joinotify só aparece em falhas que acontecem antes da chamada.

Rastreando uma requisição​

Toda resposta traz x-request-id. Se você mandar um x-request-id próprio, nós o ecoamos — o que facilita casar os logs dos dois lados.

O id próprio só é aceito se tiver de 8 a 128 caracteres, entre letras, dígitos, ., _, : e -. Fora disso geramos um nosso (um UUID) — confie sempre no que volta no header.

curl -i https://api.joinotify.com/templates \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'x-request-id: pedido-1042-tentativa-1'

Guarde esse identificador nos seus logs. É a primeira coisa que o suporte pede.

Quais erros vale repetir​

SituaçãoRepetir?
429 rate_limitSim, depois de Retry-After.
500 · 502 · 504Sim, com backoff exponencial e teto de tentativas.
409 idempotency_in_progressSim, depois de alguns segundos, com a mesma chave.
402 payment_requiredNão. Resolva o que error.action indica.
401 · 403 · 404 · 405 · outros 409Não. Repetir vai falhar igual.
400 · 413 · 415 · 422 invalid_requestNão. Corrija o payload.
422 meta_errorDepende do code da Meta. Erro de janela ou template não se resolve repetindo.