> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cfosilvia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# EMAIL UNSUBSCRIBE SYSTEM

# Email Unsubscribe System Documentation

## Overview

This system provides legally-compliant email unsubscribe functionality while protecting operational and transactional emails that users need.

## Email Categories

### 1. Marketing Emails (Can Unsubscribe) ✅ ACTIVE

When a user clicks "unsubscribe" from ANY marketing email, they are **automatically unsubscribed from ALL marketing emails**:

* ✉️ `feature_announcements` - New feature announcements and product updates
* ✉️ `marketing_general` - General marketing communications, webinars, and promotional content

**Behavior:** One-click unsubscribe disables both marketing preferences at once.

**Implementation:** Managed by `EmailSubscriptionManager` class.

### 2. Operational Emails (Managed in Settings UI) ✅ ACTIVE

Managed separately in Settings UI, independent of marketing unsubscribe:

* 📊 `emailDailySummary` - Daily portfolio snapshots (via all\_users table)
* 📈 `emailStockPriceSpikes` - Stock price movement alerts
* 💳 `emailSpendingSpikes` - Spending spike alerts
* 📄 `emailFinancialStatementSchedule` - Scheduled financial statements

**Status:** Fully implemented in Settings → Email Notifications page.
**Management:** Via `useEmailPreferences` hook and Settings UI toggles.

### 3. Transactional Emails (Always Sent) ✅ ACTIVE

These emails are **always sent** regardless of user preferences (legal requirement):

* 🔒 `security` - Security alerts and 2FA
* 👋 `welcome` - Welcome and onboarding emails
* 🗑️ `account_deletion` - Account deletion confirmations
* ⚠️ `error_notifications` - Critical error alerts

**Behavior:** Cannot be unsubscribed from.

***

## User Experience Flow

### Marketing Email Unsubscribe

1. User receives marketing email (e.g., feature announcement, webinar invite)
2. Clicks "Unsubscribe from marketing emails" link at bottom
3. Automatically unsubscribed from BOTH marketing email types:
   * `feature_announcements` = false
   * `marketing_general` = false
4. Sees confirmation page explaining they'll still receive:
   * Daily financial snapshots (if enabled)
   * Important account and security notifications
5. Can manage detailed preferences in Settings → Email Notifications

### Marketing Email Management in Settings

1. User goes to Settings → Email Notifications
2. Can toggle individual marketing preferences:
   * ☑️ Feature Announcements - New features, product updates, improvements
   * ☑️ Marketing & Promotions - Webinars, office hours, educational content
3. Both have blue "Marketing" badges to distinguish from operational emails

### Operational Email Management

1. User goes to Settings → Email Notifications
2. Can toggle individual preferences:
   * ☑️ Portfolio Summary (Daily)
   * ☑️ Spending Alerts (Weekly)
   * ☑️ Stock Price Alerts (Daily)
   * ☑️ Personal Financial Statement (Scheduled)
3. These are independent of marketing email preferences

***

## Technical Implementation

### Permission Check Before Sending

```typescript theme={null}
// Check if user allows this email type
const canSend = await emailSubscriptionManager.canSendEmail(
  clerkId,
  'marketing_general'
)

if (!canSend) {
  // Skip this user
  continue
}
```

### Generate Unsubscribe Link

```typescript theme={null}
// Generate secure token and unsubscribe URL
const unsubscribeLink = await emailSubscriptionManager.generateUnsubscribeLink(
  clerkId,
  'marketing_general'
)

// Include in email footer
<Link href={unsubscribeLink}>Unsubscribe from marketing emails</Link>
```

### Using Marketing Preferences in Settings UI

```typescript theme={null}
// In your settings component
import { useMarketingPreferences } from '@/hooks/queries/use-marketing-preferences'

const {
  preferences, // { feature_announcements: boolean, marketing_general: boolean }
  isLoading,
  updatePreference, // (field, value) => Promise<void>
  isUpdating
} = useMarketingPreferences()

// Toggle a preference
<Switch
  checked={preferences.feature_announcements}
  onCheckedChange={(checked) =>
    updatePreference('feature_announcements', checked)
  }
  disabled={isLoading || isUpdating}
/>
```

### Using Operational Email Preferences

```typescript theme={null}
// In your settings component
import { useEmailPreferences } from '@/hooks/queries/use-email-preferences'

const {
  preferences, // { emailDailySummary, emailSpendingSpikes, emailStockPriceSpikes }
  isLoading,
  updatePreference, // (type, value) => void
  isUpdating
} = useEmailPreferences()

// Toggle a preference
<Switch
  checked={preferences.emailDailySummary}
  onCheckedChange={(checked) =>
    updatePreference('emailDailySummary', checked)
  }
  disabled={isLoading || isUpdating}
/>
```

### Processing Unsubscribe

When token is clicked:

1. Validates token (not expired, not used)
2. Checks if it's a marketing email type
3. If marketing → disables BOTH marketing preferences
4. If operational → disables only that specific preference
5. Redirects to confirmation page

***

## Database Schema

### email\_preferences table

```sql theme={null}
-- Marketing (both disabled together via unsubscribe link)
feature_announcements BOOLEAN DEFAULT TRUE
marketing_general BOOLEAN DEFAULT TRUE

-- Note: Operational emails (daily summaries, alerts, etc.) are stored in all_users table,
-- not in email_preferences. They use different columns:
-- email_daily_summary, email_spending_spikes, email_stock_price_spikes, etc.
```

### unsubscribe\_tokens table

```sql theme={null}
clerk_id TEXT NOT NULL
token TEXT NOT NULL UNIQUE
email_type TEXT NOT NULL
expires_at TIMESTAMPTZ NOT NULL (90 days)
used_at TIMESTAMPTZ (null until used)
```

***

## Legal Compliance

### CAN-SPAM Act ✅

* ✅ One-click unsubscribe in all marketing emails
* ✅ Clear "Unsubscribe" language
* ✅ Processes unsubscribe within 10 business days (instant)
* ✅ Physical address in footer (Silvia contact info)

### GDPR ✅

* ✅ Users can manage all preferences
* ✅ Clear explanation of email types
* ✅ Easy opt-out mechanism
* ✅ No dark patterns

### Best Practices ✅

* ✅ Protects operational emails users need
* ✅ Clear communication about what continues
* ✅ Settings page for granular control
* ✅ Transactional emails always sent

***

## Email Footer Template

All marketing emails must include:

```html theme={null}
<footer>
  You received this email because you signed up for Silvia.
  <br />
  <a href="{unsubscribeLink}">Unsubscribe from marketing emails</a> | 
  <a href="https://cfosilvia.com/settings">Manage email preferences</a>
  <br />
  Note: You'll still receive important account notifications and daily portfolio updates (if enabled).
</footer>
```

***

## Key Files

### Backend

* `/lib/email-subscription-manager.ts` - Core EmailSubscriptionManager class
* `/app/api/unsubscribe/route.ts` - Unsubscribe token processing
* `/app/api/marketing-preferences/route.ts` - Marketing preferences CRUD
* `/sql-queries/email-preferences-schema.sql` - Database schema

### Frontend

* `/app/settings/page.tsx` - Settings page with email preference toggles
* `/app/unsubscribe/page.tsx` - Unsubscribe confirmation page
* `/hooks/queries/use-marketing-preferences.ts` - Marketing preferences hook
* `/hooks/queries/use-email-preferences.ts` - Operational email preferences hook

### Email Templates

* `/emails/feature-announcement-email.tsx` - Feature announcement template
* `/emails/daily-email.tsx` - Daily portfolio summary template

***

## Testing Checklist

Before sending marketing emails:

* [ ] Run SQL schema to create tables
* [ ] Test unsubscribe link generation
* [ ] Verify marketing email disables both marketing prefs
* [ ] Verify operational emails still work after unsubscribe
* [ ] Check confirmation page displays correctly
* [ ] Verify expired tokens are rejected
* [ ] Verify used tokens can't be reused
* [ ] Test permission checks skip unsubscribed users

***

## API Routes

### Unsubscribe System

* `GET /api/unsubscribe?token=xxx` - Process unsubscribe token and redirect to confirmation
* `POST /api/unsubscribe` - Alternative POST method for unsubscribe

### Marketing Preferences Management

* `GET /api/marketing-preferences` - Fetch user's marketing email preferences
* `PATCH /api/marketing-preferences` - Update marketing preferences (feature\_announcements, marketing\_general)

### Email Sending

* `GET /api/email/feature-announcement?secretKey=xxx` - Send feature announcement emails
* `GET /api/email/daily?secretKey=xxx` - Send daily portfolio summaries

***

## Implemented Features

✅ **Completed:**

* ✅ Email preference management UI in settings (both marketing and operational)
* ✅ Marketing preferences API endpoint
* ✅ Granular toggles for all email types
* ✅ Toast notifications on preference changes
* ✅ Real-time preference updates

🚧 **Future Enhancements:**

* [ ] Resubscribe flow for users who change their mind
* [ ] Email frequency controls (e.g., digest mode)
* [ ] A/B testing for unsubscribe language
* [ ] Analytics dashboard for email preferences
* [ ] Email preview before scheduling

***

## How to Send a Marketing Email (Complete Example)

```typescript theme={null}
// Example: Sending a feature announcement email
import { emailSubscriptionManager } from '@/lib/email-subscription-manager'
import { resend } from '@/lib/resend'
import FeatureAnnouncementEmail from '@/emails/feature-announcement-email'

export async function sendFeatureAnnouncement(userId: string, userEmail: string) {
  // 1. Check if user allows this email type
  const canSend = await emailSubscriptionManager.canSendEmail(
    userId,
    'feature_announcements'
  )
  
  if (!canSend) {
    console.log(`User ${userId} has unsubscribed from feature announcements`)
    return { skipped: true }
  }
  
  // 2. Generate unsubscribe link for this email
  const unsubscribeLink = await emailSubscriptionManager.generateUnsubscribeLink(
    userId,
    'feature_announcements'
  )
  
  // 3. Send email with unsubscribe link
  const { data, error } = await resend.emails.send({
    from: 'Silvia <no-reply@cfosilvia.com>',
    to: userEmail,
    subject: 'New Feature: Enhanced Account Types',
    react: FeatureAnnouncementEmail({ unsubscribeLink }),
  })
  
  if (error) {
    console.error('Failed to send email:', error)
    return { error }
  }
  
  return { success: true, messageId: data.id }
}
```

***

## Summary

✅ **Marketing emails** → One-click unsubscribe from ALL marketing\
✅ **Operational emails** → Managed individually in settings\
✅ **Transactional emails** → Always sent (legal/security)\
✅ **Legally compliant** → CAN-SPAM and GDPR\
✅ **User-friendly** → Clear communication about what continues\
✅ **Fully implemented** → Settings UI, API routes, and token system


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.