# Notifications Specification ## Job to Be Done When I wake up each morning, I want to receive an email with my training decision, so that I don't need to open the app to know if I should train. ## Email Notifications ### Daily Training Email Sent at user's preferred `notificationTime` (default: 07:00). **Subject:** ``` PhaseFlow: [STATUS] - Day [cycleDay] ([phase]) ``` Example: `PhaseFlow: ✅ TRAIN - Day 12 (FOLLICULAR)` **Body:** ``` Good morning! Today's Decision: [STATUS] Reason: [reason] Current Metrics: - Cycle Day: [cycleDay] ([phase]) - Body Battery: [bbCurrent] - HRV: [hrvStatus] - Week Intensity: [weekIntensity]/[phaseLimit] min Nutrition Today: - Seeds: [seeds] - Carbs: [carbRange] - Keto: [ketoGuidance] [Optional: Seed switch alert if day 15] [Optional: Token expiration warning] --- PhaseFlow - Training with your cycle, not against it ``` ### Token Expiration Warnings **14 Days Before:** Subject: `⚠️ PhaseFlow: Garmin tokens expire in 14 days` **7 Days Before:** Subject: `🚨 PhaseFlow: Garmin tokens expire in 7 days - action required` ## Email Provider Using Resend via `resend` npm package. **Configuration:** - `RESEND_API_KEY` - API key for Resend - `EMAIL_FROM` - Sender address (e.g., `PhaseFlow `) ## Email Utilities (`src/lib/email.ts`) **Functions:** - `sendDailyNotification(user, decision, dailyData)` - Send morning email - `sendTokenExpirationWarning(user, daysUntilExpiry)` - Send warning email ## Cron Jobs ### `/api/cron/notifications` Protected by `CRON_SECRET` header. **Trigger:** Daily at notification times **Process:** 1. Find all users with `notificationTime` matching current hour 2. For each user: - Fetch current decision from decision engine - Send email via Resend - Update `DailyLog.notificationSentAt` ### Timezone Handling Users are batched into hourly timezone windows for efficient scheduling. **Implementation:** 1. Cron job runs 24 times daily (once per hour, on the hour) 2. Each run queries users whose `notificationTime` hour matches the current UTC hour adjusted for their timezone 3. Users are processed in batches within their timezone window **Example:** - User A: timezone `America/New_York`, notificationTime `07:00` - User B: timezone `America/Los_Angeles`, notificationTime `07:00` - User C: timezone `Europe/London`, notificationTime `07:00` When cron runs at 12:00 UTC: - User A receives notification (12:00 UTC = 07:00 EST) - User B skipped (12:00 UTC = 04:00 PST) - User C skipped (12:00 UTC = 12:00 GMT) **Query Logic:** ```sql SELECT * FROM users WHERE EXTRACT(HOUR FROM NOW() AT TIME ZONE timezone) = CAST(SUBSTRING(notificationTime, 1, 2) AS INT) ``` ## Rate Limiting - Max 1 notification per user per day - Check `DailyLog.notificationSentAt` before sending - Only send if null or different date ## Success Criteria 1. Email arrives within 5 minutes of scheduled time 2. Email contains all relevant metrics and guidance 3. Token warnings sent at 14 and 7 day thresholds 4. No duplicate notifications on same day ## Acceptance Tests - [ ] Daily email contains decision status and reason - [ ] Daily email includes nutrition guidance - [ ] Seed switch alert included on day 15 - [ ] Token warning email sent at 14-day threshold - [ ] Token warning email sent at 7-day threshold - [ ] Duplicate notifications prevented - [ ] Timezone conversion correct for notification time - [ ] CRON_SECRET required for endpoint access