Onde olhar primeiro
Um envio com falha nunca devolve um erro na própria chamada POST /v1/apps/{app_id}/notifications - essa chamada apenas confirma que o Notti aceitou a requisição. O resultado da entrega é por dispositivo e é consultado em GET /v1/apps/{app_id}/notifications/{id}/deliveries. Cada entrega tem um status (sent / failed / pending) e, em caso de falha, uma string last_error.
last_error é o texto de erro bruto do provedor, não um código de erro estável - a redação exata difere entre APNs e FCM e pode mudar se o provedor alterar o texto da própria mensagem. Faça a correspondência por substrings / campos específicos do provedor listados abaixo; não espere um enum fixo.
Cancelamento automático de inscrição: o que device.invalid significa
Quando um provedor informa que um token de push é permanentemente inválido (veja as duas tabelas abaixo), o Notti faz duas coisas automaticamente, antes de você tomar qualquer ação:
- Define
subscribedcomofalsenesse dispositivo. - Dispara um evento de webhook
device.invalidem vez denotification.failed.
Isso não é um bug nem uma falha intermitente - significa que o provedor informou ao Notti que o token nunca mais vai funcionar (app desinstalado, token revogado, gateway errado - veja abaixo). Reinscrever sem um token novo e válido vindo do dispositivo vai falhar novamente da mesma forma. O seu handler de webhook é o sinal confiável aqui - não infira "permanentemente inválido" apenas pelo texto de last_error; use o tipo de evento device.invalid.
APNs: permanente vs com nova tentativa
Motivo do APNs (aparece dentro de last_error) | Resultado | O que geralmente significa |
|---|---|---|
BadDeviceToken | Permanente - inscrição cancelada automaticamente | Token malformado, revogado ou (muito comumente) enviado ao gateway errado - veja Testes: sandbox vs produção |
Unregistered | Permanente - inscrição cancelada automaticamente | O app foi desinstalado do dispositivo |
ExpiredToken | Permanente - inscrição cancelada automaticamente | O token não é mais válido |
DeviceTokenNotForTopic | Permanente - inscrição cancelada automaticamente | O token pertence a um bundle ID diferente do das credenciais APNs deste App |
TooManyRequests, InternalServerError, ServiceUnavailable, Shutdown, ExpiredProviderToken | Com nova tentativa | Problema transitório no lado do APNs ou token de autenticação do provedor expirado (reenvie as credenciais APNs se persistir) |
| Qualquer outra string de motivo | Tratado como permanente | O Notti não tenta novamente motivos do APNs que não reconhece |
Se você vê BadDeviceToken em todo envio de um App específico, confira o apns_environment desse App (sandbox vs production, o environment que você definiu ao salvar as credenciais APNs) em relação a como o build do cliente que gerou o token foi assinado - essa é a causa mais comum, não uma integração quebrada.
FCM: permanente vs com nova tentativa
Código de erro do FCM (aparece dentro de last_error) | Resultado | O que geralmente significa |
|---|---|---|
UNREGISTERED | Permanente - inscrição cancelada automaticamente | O app foi desinstalado, ou o token foi rotacionado no cliente sem novo registro |
INVALID_ARGUMENT | Permanente - inscrição cancelada automaticamente | Token malformado, ou token de um projeto Firebase diferente do das credenciais configuradas no App |
UNAVAILABLE, QUOTA_EXCEEDED | Com nova tentativa | Problema transitório no lado do FCM ou você atingiu a própria cota de envio do FCM |
HTTP 429, 500, 503 sem nenhum código de erro correspondente acima | Com nova tentativa | Igual ao anterior, classificado pelo status HTTP quando o FCM omite um código específico |
| Qualquer outro código | Tratado como permanente | O Notti não tenta novamente códigos do FCM que não reconhece |
provider_not_configured
Um valor distinto de last_error, não relacionado ao token em si: o App não tem credenciais APNs configuradas para um dispositivo iOS, ou não tem credenciais FCM para um dispositivo Android. Corrija concluindo a configuração de credenciais dessa plataforma - o token do dispositivo não é o problema.
Falhas com nova tentativa
Uma entrega failed com um erro que admite nova tentativa (veja as tabelas acima) não significa que você precise reenviar manualmente - o worker do Notti tenta novamente essas falhas com backoff por conta própria. Reenviar você mesmo a mesma notificação lógica corre o risco de duplicá-la; use uma Idempotency-Key se precisar repetir a chamada da API com segurança.