The delivery chain
Every push goes through the same path, and a fault at any link looks identical to the member (nothing arrives). Know the chain before you debug:
- Device registers. The installed app asks for notification permission, then obtains a Firebase registration token. A raw APNs token is never stored — the sender cannot use one.
- Token is saved to
push_tokensagainst user, gym, platform and a stable per-installdevice_id. One phone occupies one row; reinstalls replace rather than accumulate. - Something triggers a send — a class cancellation, a cover request, an alert, an appointment, a maintenance fault.
- The
send-push-notificationfunction resolves the recipients' tokens, respects their per-category notification preferences, and hands the payload to the FCM relay. - The relay (Cloud Run) signs the request with the Firebase service account and calls FCM HTTP v1.
- In parallel,
persistNotificationswrites the same message in-app, so the bell and Activity list show it even if the device push fails.
Deep links
Payloads carry a route (historically url or deep_link). Tapping stores the route and navigates once the session is ready, so a cold start launched from a notification still lands in the right place. Only /member and /dashboard routes are accepted — anything else is ignored by design.
Preferences
Members choose which categories they receive. Urgent and warning alerts are mandatory and cannot be switched off; informational ones can. Do not promise a gym that a member can be forced to receive optional categories.
Token hygiene
Tokens go stale when apps are reinstalled or uninstalled. prune-push-tokens clears out dead registrations so the platform does not report phantom devices. If a gym insists someone "has the app" but no device shows, the usual cause is that the member never granted permission or is signed in on the web only.
Testing
Settings → Push Notifications (/platform/push-notifications) sends a test to a chosen user and reports the per-device outcome — delivered, rejected, or no token. Start here before touching anything else.