Push Notifications
Push notifications support upcoming payment reminders.
Required Environment
Configure all of these for reminders:
VAPID_PUBLIC_KEYVAPID_PRIVATE_KEYVAPID_SUBJECTCRON_SECRETNOTIFICATION_SCHEDULEwhen using the Compose scheduler (cron expression, UTC, default0 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.