Push Notifications
The platform uses different notification mechanisms for each frontend. Telegram relies on its native message delivery, while the PWA uses the Web Push API with VAPID authentication.
Telegram
Telegram survey delivery is straightforward: the bot sends messages directly via the Telegram Bot API.
How it works
- Scheduler or API triggers a survey for a participant
- Bot calls
send_message()on theTelegramChannel, which sends an inline keyboard with the first question - Participant taps a button → callback reaches the bot → next question is sent
- The server controls the entire flow — each response triggers a server action
Telegram handles its own push delivery. When the bot sends a message, Telegram notifies the participant via the Telegram app's native push system. No additional configuration is needed.
Delivery paths
| Trigger | How it reaches the participant |
|---|---|
| Scheduled (cron) | APScheduler fires → start_survey_for_participant() → Telegram message |
API (POST /api/v1/trigger) | HTTP request → same function → Telegram message |
| Rule engine | Wearable webhook data → condition match → Telegram message |
PWA (Web Push)
The PWA uses the Web Push API with VAPID (Voluntary Application Server Identification) keys. Push notifications wake the service worker, which displays a native OS notification even when the browser is closed.
Architecture
Setup
-
Generate VAPID keys:
npx web-push generate-vapid-keys -
Add to
.env:VAPID_PUBLIC_KEY=BL... # Base64-encoded public key
VAPID_PRIVATE_KEY=... # Base64-encoded private key
VAPID_CLAIMS_EMAIL=mailto:you@example.com -
The PWA automatically subscribes when the participant enables notifications.
Subscription flow
- PWA fetches the VAPID public key:
GET /api/v1/webapp/push/vapid-key - Browser creates a push subscription via
pushManager.subscribe() - PWA sends the subscription to the server:
POST /api/v1/webapp/push/subscribe - Server stores it in the
push_subscriptionstable (one subscription per participant)
Subscription storage
push_subscriptions (
participant_id INTEGER PRIMARY KEY,
endpoint TEXT NOT NULL, -- Push service URL (e.g., FCM endpoint)
p256dh TEXT NOT NULL, -- Encryption public key
auth TEXT NOT NULL, -- Auth secret
created_at TEXT NOT NULL
)
Sending notifications
When a survey is triggered for a PWA participant who is offline (no active WebSocket):
- Server calls
send_push_notification(participant_id, survey_id, db, settings) - Looks up the subscription from the database
- Builds a JSON payload with survey-specific metadata (title, body, icon)
- Sends via
pywebpushusing the VAPID credentials - Creates a pending survey session regardless of push delivery success
Survey-specific notification metadata
Notifications are customized per survey type in src/push.py:
| Survey ID | Title | Body |
|---|---|---|
morning | Good Morning | Time for your morning check-in |
momentary_checkin | Check-in Time | A quick momentary assessment is ready |
evening | Good Evening | Time for your evening check-in |
| Other surveys | New Survey | You have a new survey to complete |
Error handling
- 410 Gone: Subscription expired — automatically deleted from the database
- Other errors: Logged, but the survey session is still created so the participant can complete it when they next open the app
API endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/webapp/push/vapid-key | Returns the VAPID public key |
POST | /api/v1/webapp/push/subscribe | Stores a push subscription (requires webapp token) |
DELETE | /api/v1/webapp/push/subscribe | Removes a push subscription |
Comparison
| Feature | Telegram | PWA |
|---|---|---|
| Notification delivery | Telegram app (native) | Web Push API (OS-level) |
| Server configuration | None (handled by Telegram) | VAPID keys required |
| Works when app closed | Yes | Yes (service worker) |
| Survey flow control | Server-driven (one question at a time) | Client-driven (preloaded offline) |
| Offline completion | No | Yes (responses queued locally) |