Skip to main content

Push Notifications

Push notifications support upcoming payment reminders.

Required Environment​

Configure all of these for reminders:

  • VAPID_PUBLIC_KEY
  • VAPID_PRIVATE_KEY
  • VAPID_SUBJECT
  • CRON_SECRET
  • NOTIFICATION_SCHEDULE when using the Compose scheduler (cron expression, UTC, default 0 8 * * *)

If any of the three VAPID values is missing, GET /api/push/vapid-public-key answers 503 with { "key": null }, and the Settings UI shows push as not set up and keeps the toggle disabled.

Subscription Flow​

The Settings UI checks browser support, requests notification permission, fetches /api/push/vapid-public-key, subscribes through PushManager, and stores the endpoint through POST /api/push/subscribe.

Users can disable reminders. The browser unsubscribes and the app removes the endpoint with DELETE /api/push/subscribe.

Reminder Cron Flow​

GET /api/cron/upcoming-payments requires:

Authorization: Bearer <CRON_SECRET>

The route answers 500 if CRON_SECRET is not set and 401 on a wrong token, and returns { success, notified, total }. The scheduler and the second job (/api/cron/insights, INSIGHTS_SCHEDULE) are described in Scheduled Jobs.

The cron job checks recurring expenses due tomorrow (the next UTC day) and sends one push notification per due expense. The notification opens /expenses.

Expired or invalid push subscriptions returning 404 or 410 are removed during send attempts.

Known Limitation​

POST /api/push/subscribe only checks that the endpoint is a non-empty string. It is not validated as a push service URL, so a user could store an arbitrary URL that the reminder job later calls. This is tracked in the repository's open findings.