ZeepNotti Docs

Troubleshooting

Reading Delivery errors, auto-unsubscribe, and common failure causes.

Where to look first

A failed send never throws an error back from POST /v1/apps/{app_id}/notifications itself - that call only confirms ZeepNotti accepted the request. Delivery outcome is per-Device, checked via GET /v1/apps/{app_id}/notifications/{id}/deliveries. Each Delivery has a status (sent / failed / pending) and, on failure, a last_error string.

last_error is the raw provider error text, not a stable error code - its exact wording differs between APNs and FCM and can change if the provider changes its own message text. Match on substrings / provider-specific fields below, don't expect a fixed enum.

Auto-unsubscribe: what device.invalid means

When a provider reports a push token as permanently invalid (see the two tables below), ZeepNotti does two things automatically, before you take any action:

  1. Sets that Device's subscribed to false.
  2. Dispatches a device.invalid webhook event instead of notification.failed.

This is not a bug or a flaky failure - it means the provider told ZeepNotti the token will never work again (app uninstalled, token revoked, wrong gateway - see below). Re-subscribing without a fresh, valid token from the device will just fail again the same way. Your webhook handler is the reliable signal here - don't infer "permanently invalid" from last_error text alone, key off the device.invalid event type.

APNs: permanent vs retryable

APNs reason (appears inside last_error)OutcomeWhat it usually means
BadDeviceTokenPermanent - auto-unsubscribedToken malformed, revoked, or (very commonly) sent to the wrong gateway - see Testing: sandbox vs production
UnregisteredPermanent - auto-unsubscribedApp was uninstalled from the device
ExpiredTokenPermanent - auto-unsubscribedToken no longer valid
DeviceTokenNotForTopicPermanent - auto-unsubscribedToken belongs to a different bundle ID than this App's APNs credentials
TooManyRequests, InternalServerError, ServiceUnavailable, Shutdown, ExpiredProviderTokenRetryableTransient APNs-side issue or an expired provider auth token (re-upload APNs credentials if this persists)
Any other reason stringTreated as permanentZeepNotti does not retry unrecognized APNs reasons

If you're seeing BadDeviceToken on every send from a specific App, check that App's apns_environment (sandbox vs production) against how the client build that produced the token was signed - this is the single most common cause, not a broken integration.

FCM: permanent vs retryable

FCM error code (appears inside last_error)OutcomeWhat it usually means
UNREGISTEREDPermanent - auto-unsubscribedApp was uninstalled, or the token was rotated on the client without re-registering
INVALID_ARGUMENTPermanent - auto-unsubscribedMalformed token, or a token from a different Firebase project than the App's configured credentials
UNAVAILABLE, QUOTA_EXCEEDEDRetryableTransient FCM-side issue or you've hit FCM's own sending quota
HTTP 429, 500, 503 with no matching error code aboveRetryableSame as above, classified by HTTP status when FCM omits a specific code
Any other codeTreated as permanentZeepNotti does not retry unrecognized FCM codes

provider_not_configured

A distinct last_error value, unrelated to the token itself: the App has no APNs credentials configured for an iOS Device, or no FCM credentials for an Android Device. Fix by completing credential setup for that platform - the Device's token isn't the problem.

Retryable failures

A failed Delivery with a retryable error (see the tables above) does not mean you need to resend manually - ZeepNotti' worker retries these with backoff on its own. Resending the same logical notification yourself risks a duplicate; use an Idempotency-Key if you do need to safely retry the API call itself.

On this page