Notifications (Email + Scheduled)
Status: Shipped — Resend-backed transactional email + nestjs/schedule cron jobs. Last reviewed: 2026-05-28
What
Scheduled outbound email notifications via Resend:
- Class reminders — daily 08:00 UTC cron picks tomorrow’s sessions and emails confirmed bookings.
- Subscription expiration warnings — daily 09:00 UTC cron warns members whose subscription expires in 6–7 days.
- Payment reminders — payment-related transactional emails.
- Cancellation notifications — booking / class cancellation emails (separate service).
Distinct from:
../push-notifications/— mobile push, separate stack (Expo + BullMQ).../announcements/— coach broadcasts.../messages-comments/— in-app chat.../event-tracking/— analytics, not user notifications.
Why
Email is the lowest-common-denominator nudge channel for not-yet-installed mobile members and a parallel backup to push for installed members. Class reminders are required for booking retention.
Personas
| Persona | Surface | Capabilities |
|---|---|---|
| System (cron) | n/a | Auto-sends reminders |
| Member | Inbox | Receives the email |
No user-facing notification center exists today — notifications are write-once email events, not stored in a table for in-app retrieval.
Capabilities
- Resend client — global
RESEND_CLIENTtoken;from = RESEND_FROM_ADDRESSenv or'FitKit <noreply@fitkit.fit>'. - Send wrapper —
EmailService.send({to, subject, html})swallows Resend errors and logs them. - Test sink — in
NODE_ENV=test,EmailServiceretains sent emails in-memory for spec assertions. - Cron jobs —
NotificationSchedulerServiceregisters@Cron('0 8 * * *')(class reminders),@Cron('0 9 * * *')(expiration warnings), more incancellation-notifications.service. - HTML templates —
apps/api/src/notifications/templates/hand-written HTML helpers per email kind. - Retry wrapper —
runWithRetryincommon/cron-retry.tsfor transient failures. - Cron gate —
cronsEnabled()toggle env-driven; off in dev by default.
Capabilities (gaps)
- No in-app notification feed / notification center.
- No per-user email opt-out for transactional categories (only push prefs exist).
- No SMS / WhatsApp channel.
- No batching / digest support.
- No localization in the email templates (HTML is currently English-first; verify per template).
Related
../scheduling-bookings/— class reminders read fromclass_sessions+bookings.../subscriptions-plans/— expiration source.../push-notifications/— parallel channel.../legal/— TOS update could trigger an email broadcast (not yet wired).
Source code
- API:
apps/api/src/notifications/email.service.ts— Resend wrappernotification-scheduler.service.ts— cron jobscancellation-notifications.service.ts— booking cancellation emailstemplates/—class-reminder,expiration-warning,payment-reminder,cancellation
- DB: no notifications table; persistence lives in the source domain (bookings, subscriptions)