Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesPush NotificationsPush Notifications — Code Map

Push Notifications — Code Map

API

FileRole
apps/api/src/push-notifications/push-notifications.module.tsGlobal; BullMQ queue notifications-push; controllers + processor
apps/api/src/push-notifications/push-notifications.service.tsToken lifecycle + send + schedule. Constants: PUSH_QUEUE_NAME = 'notifications-push'. filterDeliveryTokens returns DeliveryToken[] ({ token, expoProjectId }); registerToken validates and stores the device’s EAS project
apps/api/src/push-notifications/push-notifications.processor.tsBullMQ worker; uses expo-server-sdk; groups by Expo project, chunks, pairs {ticket, token} per chunk, schedules receipt check
apps/api/src/push-notifications/push-notifications.controller.tsPOST /devices/register, DELETE /devices/:token
apps/api/src/push-notifications/notification-prefs.controller.tsGET/PATCH /users/me/notification-preferences
apps/api/src/push-notifications/dto/register-device.dto.ts{ expoPushToken, platform: 'ios' | 'android', appId?, locale?, expoProjectId? }
apps/api/src/push-notifications/notification-copy.tsLocalized push copy builders (pushCopy.*), incl. billingDebtOpened, billingDebtLastDay, billingDebtCancelled, billingCardExpiring, billingCardUnusable (plain-date pay-by days, copy under pushNotifications.copy.billing) and trialBrief (copy under trialsPush)
apps/api/src/notifications/billing-debt-notifications.service.tsBillingDebtNotificationsService, sender of the billing category (debt opened / last day / cancelled, card-expiry reminders, card-unusable notice)
apps/api/src/trials/trial-brief.service.tsTrialBriefService, sender of the trials category (coach brief before a class with trials)

DB

FileRole
libs/db/src/lib/schema/device-tokens.tsdevice_tokens table
libs/db/src/lib/schema/notification-prefs.tsnotification_prefs (JSONB)

Shared

FileRole
libs/shared/src/lib/schemas/notification-prefs.tsNOTIFICATION_CHANNELS, NOTIFICATION_CATEGORIES, NotificationPrefsValue, isChannelOptedOut, isChannelEnabled
libs/shared/src/lib/branding.tsCONTAINER_EAS_PROJECT_ID, LEGACY_CONTAINER_EAS_PROJECT_IDS, CONTAINER_EAS_PROJECT_IDS, easProjectIdSchema — the projects a container token may legitimately claim
apps/api/src/notifications/email-types.tsEMAIL_TYPES, prefCategoryFor — the email-type → preference-category gate table (CARD_EXPIRING, CARD_UNUSABLE, DEBT_OPENED, DEBT_LAST_DAY, DEBT_CANCELLED → billing)

Mobile / web

  • Mobile app calls register on boot + revoke on sign-out. Source lives in the mobile repo (not in this monorepo): src/hooks/use-push-notifications.ts sends expoProjectId — the same projectId it passed to getExpoPushTokenAsync — so the API can record provenance. Older builds omit it and keep whatever is already stored.
  • Web has no push surface (PWA shell only via apps/web/src/components/pwa/).

Config

EnvPurpose
EXPO_ACCESS_TOKENOptional but recommended for FCM v1 backend; passed to new Expo({ accessToken })
Redis / BullMQ credsInherited from app-wide queue config