# 14. Backend Router — Email, Push, and SMS Notifications meta: id: shieldai-unified-restructure-14 feature: shieldai-unified-restructure priority: P1 depends_on: [shieldai-unified-restructure-11] tags: [backend, notifications, email, push, sms, api] objective: - Build the tRPC router and service layer for sending notifications across all channels: email (Resend), push (FCM/APNs), and SMS (Twilio). Port logic from `packages/shared-notifications/` into a unified `notifications` router. deliverables: - `web/src/server/api/routers/notification.ts` — Notification router: - `notification.sendEmail` — `adminProcedure` (or internal service use) sending transactional email - `notification.sendPush` — `protectedProcedure` sending push to user's devices - `notification.sendSMS` — `protectedProcedure` sending SMS - `notification.registerDevice` — `protectedProcedure` registering FCM/APNs token - `notification.unregisterDevice` — `protectedProcedure` removing device token - `notification.listDevices` — `protectedProcedure` listing registered devices - `notification.getPreferences` — `protectedProcedure` getting notification preferences - `notification.updatePreferences` — `protectedProcedure` updating preferences - `web/src/server/services/notification.service.ts` — Business logic: - `sendEmail(to, subject, html, text?)` — via Resend API - `sendPush(deviceTokens, title, body, data?)` — via FCM for Android, APNs for iOS - `sendSMS(phoneNumber, message)` — via Twilio - `registerDevice(userId, token, platform, deviceType)` — save to DB - `unregisterDevice(userId, token)` — soft-delete or mark inactive - `getPreferences(userId)` — read from DB - `updatePreferences(userId, prefs)` — write to DB - `web/src/server/services/email.templates.ts` — Email template renderer: - Welcome email - Alert notification email - Password reset email - Family invite email - Billing receipt email - Provider clients: - `web/src/server/lib/resend.ts` — Resend client - `web/src/server/lib/fcm.ts` — Firebase Admin SDK initialization - `web/src/server/lib/twilio.ts` — Twilio client steps: 1. Install dependencies in `web/`: - `resend` (email) - `firebase-admin` (push notifications) - `twilio` (SMS) 2. Create provider client files: - `resend.ts`: initialize with `RESEND_API_KEY` - `fcm.ts`: initialize Firebase Admin with service account JSON - `twilio.ts`: initialize with `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` 3. Create `web/src/server/services/email.templates.ts`: - Each template is a function returning `{ subject, html, text }` - Use simple HTML with inline CSS (or a minimal template engine) - Include ShieldAI branding (logo, colors) 4. Create `web/src/server/services/notification.service.ts`: - `sendEmail`: call Resend, log result, handle errors - `sendPush`: iterate device tokens, call FCM for Android tokens, APNs for iOS tokens (or use Firebase Admin which handles both) - `sendSMS`: call Twilio Messages API, validate E.164 phone format - `registerDevice`: upsert into `DeviceToken` table - `unregisterDevice`: set `isActive = false` - `getPreferences` / `updatePreferences`: read/write a `NotificationPreferences` table (add to schema if missing) 5. Create `web/src/server/api/routers/notification.ts`: - Define Zod schemas for all inputs - `sendEmail`: restricted to admin or internal service calls - `sendPush`: accepts `title`, `body`, `data` payload; sends to all active user devices - `sendSMS`: accepts `phoneNumber`, `message` - Device registration/unregistration procedures - Preference get/update procedures 6. Wire router into `web/src/server/api/root.ts`. 7. Write unit tests with mocked providers. steps: - Unit: `sendEmail` calls Resend with correct parameters - Unit: `sendPush` iterates tokens and calls FCM - Unit: `sendSMS` validates E.164 format before calling Twilio - Unit: `registerDevice` creates DeviceToken record - Unit: `unregisterDevice` marks token inactive - Integration: `notification.registerDevice` persists token to DB acceptance_criteria: - [ ] Email can be sent via Resend with branded templates - [ ] Push notifications can be sent to registered Android and iOS devices via FCM - [ ] SMS can be sent via Twilio to valid E.164 numbers - [ ] Device tokens can be registered and unregistered per user - [ ] Notification preferences are persisted and respected (e.g., user can disable SMS alerts) - [ ] All provider errors are caught and logged without crashing the app - [ ] Email templates include ShieldAI branding validation: - Send a test email via Resend to your own address and verify receipt - Register a test device token and send a push (use Firebase Console or API) - Send a test SMS via Twilio (use test credentials for free) - Run `cd web && pnpm test` for notification service tests notes: - Reference legacy: `packages/shared-notifications/src/`, `packages/api/src/routes/notifications.routes.ts` - For FCM, Firebase Admin SDK requires a service account JSON. Store path in `FIREBASE_SERVICE_ACCOUNT_PATH` env var. - For APNs, Firebase Admin can proxy to APNs if configured correctly. Alternatively, use `apn` npm package for direct APNs integration. - Twilio test credentials (`TWILIO_TEST_*`) can be used for integration tests without sending real SMS. - Resend has a generous free tier (100 emails/day) perfect for development. - Consider adding a `notification.queue` table for reliable delivery (retry failed sends). This can be a follow-up task. - The `NotificationPreferences` model may need to be added to the Drizzle schema if not already present. Add it as part of this task.