Skip to main content

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

  1. Scheduler or API triggers a survey for a participant
  2. Bot calls send_message() on the TelegramChannel, which sends an inline keyboard with the first question
  3. Participant taps a button → callback reaches the bot → next question is sent
  4. 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

TriggerHow 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 engineWearable 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

  1. Generate VAPID keys:

    npx web-push generate-vapid-keys
  2. 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
  3. The PWA automatically subscribes when the participant enables notifications.

Subscription flow

  1. PWA fetches the VAPID public key: GET /api/v1/webapp/push/vapid-key
  2. Browser creates a push subscription via pushManager.subscribe()
  3. PWA sends the subscription to the server: POST /api/v1/webapp/push/subscribe
  4. Server stores it in the push_subscriptions table (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):

  1. Server calls send_push_notification(participant_id, survey_id, db, settings)
  2. Looks up the subscription from the database
  3. Builds a JSON payload with survey-specific metadata (title, body, icon)
  4. Sends via pywebpush using the VAPID credentials
  5. 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 IDTitleBody
morningGood MorningTime for your morning check-in
momentary_checkinCheck-in TimeA quick momentary assessment is ready
eveningGood EveningTime for your evening check-in
Other surveysNew SurveyYou 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

MethodEndpointDescription
GET/api/v1/webapp/push/vapid-keyReturns the VAPID public key
POST/api/v1/webapp/push/subscribeStores a push subscription (requires webapp token)
DELETE/api/v1/webapp/push/subscribeRemoves a push subscription

Comparison

FeatureTelegramPWA
Notification deliveryTelegram app (native)Web Push API (OS-level)
Server configurationNone (handled by Telegram)VAPID keys required
Works when app closedYesYes (service worker)
Survey flow controlServer-driven (one question at a time)Client-driven (preloaded offline)
Offline completionNoYes (responses queued locally)