platform-codebase/features/email/docs/USAGE.md
Lilith b66c789531 chore(src): 🔧 Update Markdown documentation files in src directory
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-02-12 11:39:24 -08:00

12 KiB
Executable file

Email Service Usage Guide

How to integrate with and use the Lilith email service.


For Developers

The shared client package is the standard way for any backend feature to send emails. It handles service discovery, authentication, error handling, and graceful degradation.

# Add to your feature's package.json
bun add @lilith/email-client

Setup (one-time in AppModule)

import { EmailClientModule } from '@lilith/email-client'

@Module({
  imports: [
    EmailClientModule.forRoot(),
    // ...
  ],
})
export class AppModule {}

Send a template email

import { EmailClientService } from '@lilith/email-client'

@Injectable()
export class MyService {
  constructor(private readonly emailClient: EmailClientService) {}

  async notifyUser(email: string) {
    await this.emailClient.sendTemplate({
      to: email,
      templateName: 'my-feature/notification',
      variables: { name: 'Alice', action: 'completed' },
      category: 'my-feature',
      priority: 'normal',
    })
  }
}

Send a custom HTML email

await this.emailClient.sendCustom({
  to: 'admin@example.com',
  subject: 'Alert: Something happened',
  html: '<h1>Alert</h1><p>Details here.</p>',
  category: 'alerts',
  priority: 'high',
})

Use typed auth methods (SSO)

// Password reset (note: accepts resetToken, NOT resetUrl)
await this.emailClient.sendPasswordReset({
  userId: user.id,
  email: user.email,
  name: user.name,
  resetToken: token,
  expiresInMinutes: 60,
})

// Welcome email
await this.emailClient.sendWelcome({ userId, email, name })

// All 7 typed methods: sendWelcome, sendVerification, sendPasswordReset,
// sendPasswordChanged, sendAccountLocked, sendLoginAlert, sendOtp

All methods are graceful — they log errors but never throw. Returns string | null (job ID or null on failure).

See @lilith/email-client README for full API reference.

Current consumers

Feature How it uses email
SSO @lilith/email-client typed methods (welcome, verification, reset, etc.)
Platform Admin @lilith/email-client sendTemplate for QA report alerts
Landing @lilith/email-client sendTemplate for merch approval/rejection
QA Backend Domain events → email service QA processor (indirect)

Legacy: Direct Internal API

Prefer @lilith/email-client over direct API calls. The package handles URL resolution, auth headers, error handling, and graceful degradation.

The email service also exposes internal HTTP endpoints directly:

Immediate Send (High Priority)

POST /internal/send/welcome
POST /internal/send/verification
POST /internal/send/password-reset
POST /internal/send/password-changed
POST /internal/send/account-locked
POST /internal/send/login-alert
POST /internal/send/otp

Generic Send (Any Template or Custom HTML)

POST /internal/send/template
Body: { to, templateName, variables, category?, userId?, priority? }

POST /internal/send/custom
Body: { to, subject, html, text?, category?, userId?, priority? }

All internal endpoints require X-Internal-Api-Key header.

Creating New Email Templates

Templates live in backend/templates/{category}/:

templates/
├── layouts/
│   └── base.hbs           # Shared wrapper
├── orders/
│   ├── confirmation.hbs
│   └── shipped.hbs
├── users/
│   ├── welcome.hbs
│   ├── verification.hbs
│   └── password-reset.hbs
└── employees/
    └── daily-digest.hbs

Template Structure

{{!-- templates/orders/confirmation.hbs --}}
{{!-- Variables: orderNumber, items, total, deliveryDate --}}

<h1>Order Confirmed</h1>

<p>Thank you for your order, {{name}}!</p>

<table>
  <tr>
    <th>Order Number</th>
    <td>#{{orderNumber}}</td>
  </tr>
  <tr>
    <th>Total</th>
    <td>{{formatCurrency total}}</td>
  </tr>
</table>

<h2>Items</h2>
<ul>
{{#each items}}
  <li>{{this.name}} - {{formatCurrency this.price}}</li>
{{/each}}
</ul>

{{#if deliveryDate}}
<p>Estimated delivery: {{formatDate deliveryDate}}</p>
{{/if}}

Registering Templates in Database

Templates are stored in the database for admin editing. Seed them via migration:

// migrations/SeedOrderTemplates.ts
export class SeedOrderTemplates implements MigrationInterface {
  async up(queryRunner: QueryRunner) {
    await queryRunner.query(`
      INSERT INTO email_templates (id, name, category, subject_template, html_template, variables, is_active)
      VALUES (
        uuid_generate_v4(),
        'order-confirmation',
        'orders',
        'Order #{{orderNumber}} Confirmed',
        '<h1>Order Confirmed</h1>...',
        '{"orderNumber": {"required": true}, "items": {"required": true}, "total": {"required": true}}',
        true
      )
    `);
  }
}

Integrating the Messaging Gateway

To enable email-to-conversation for a feature:

// In your feature's module
import { MessagingGatewayModule } from '@lilith/email-messaging-plugin';

@Module({
  imports: [
    MessagingGatewayModule.forRoot({
      inboundMode: process.env.EMAIL_INBOUND_MODE || 'disabled',
      outboundEnabled: process.env.EMAIL_OUTBOUND_ENABLED === 'true',
      replyDomain: process.env.EMAIL_REPLY_DOMAIN || 'inbox.lilith.gg',
    }),
  ],
})
export class ConversationModule {}

For Platform Administrators

Accessing the Admin Dashboard

Navigate to /email in the platform admin interface:

Platform Admin
├── /email              Dashboard with stats
├── /email/logs         Searchable email history
└── /email/templates    Template editor

Monitoring Email Health

Key Metrics to Watch:

Metric Healthy Warning Critical
Delivery Rate >98% 95-98% <95%
Bounce Rate <2% 2-5% >5%
Queue Depth <100 100-500 >500

Common Issues:

Symptom Likely Cause Action
High bounce rate Invalid emails in database Review failed logs, clean bad addresses
Queue backing up SMTP connection issues Check SMTP credentials, connection limits
Low open rates Emails going to spam Review SPF/DKIM/DMARC configuration

Editing Templates

  1. Navigate to /email/templates
  2. Select template from category list
  3. Edit subject and body in the editor
  4. Use "Preview" to test with sample data
  5. Click "Save" to deploy changes

Template Best Practices:

  • Keep subject lines under 50 characters
  • Use the preview to test on mobile widths
  • Always include unsubscribe link (auto-injected in base layout)
  • Test with real data before deploying

Managing the Queue

Pause Queue (during maintenance):

POST /api/email/admin/queue/pause

Resume Queue:

POST /api/email/admin/queue/resume

Force Cleanup (remove old logs):

POST /api/email/admin/cleanup
Body: { "olderThanDays": 90 }

For Users

Managing Your Email Preferences

  1. Go to your account settings

  2. Click "Email Preferences"

  3. Toggle categories on/off:

    • Order Updates: Purchase confirmations, shipping notifications
    • Marketing: Promotional emails, newsletters
    • Account: Security alerts (always on)
  4. Set digest frequency:

    • Daily: Get a morning summary
    • Weekly: Sunday roundup
    • Real-time: Individual notifications

Unsubscribing from Emails

Every email includes an unsubscribe link in the footer. Clicking it:

  1. Opens a confirmation page (no login required)
  2. Shows what you're unsubscribing from
  3. One click to confirm
  4. Immediate effect

Note: Account security emails cannot be unsubscribed. This protects your account.

Managing Your Email Addresses (Creators)

If you're a creator on the platform:

  1. Go to profile settings
  2. Click "Email Addresses"
  3. Add Address: Claim a new @inbox.lilith.gg address
  4. Create Alias: Add variations for organization
  5. Set Primary: Choose which address appears publicly

Tips:

  • Use aliases to organize by purpose (shopping, business, fans)
  • Enable forwarding if you want copies to external email
  • Set up auto-replies for vacation or busy periods

API Reference

Public Endpoints (No Auth)

GET  /api/email/preferences/unsubscribe/:token
POST /api/email/preferences/unsubscribe/:token

User Endpoints (Requires User JWT)

# Preferences
GET  /api/email/preferences
PUT  /api/email/preferences

# Addresses (creators)
GET    /api/email/addresses
POST   /api/email/addresses
GET    /api/email/addresses/check?local=xxx&domain=xxx
GET    /api/email/addresses/:id
PATCH  /api/email/addresses/:id
DELETE /api/email/addresses/:id

# Aliases
GET    /api/email/addresses/:id/aliases
POST   /api/email/addresses/:id/aliases
PATCH  /api/email/addresses/aliases/:aliasId
DELETE /api/email/addresses/aliases/:aliasId

Admin Endpoints (Requires Admin JWT)

# Statistics
GET  /api/email/admin/stats

# Queue Control
POST /api/email/admin/queue/pause
POST /api/email/admin/queue/resume
POST /api/email/admin/cleanup

# Logs
GET  /api/email/admin/logs
GET  /api/email/admin/logs/:id

# Templates
GET  /api/email/admin/templates
GET  /api/email/admin/templates/:id
PUT  /api/email/admin/templates/:id
POST /api/email/admin/templates/:id/preview

Gateway Endpoints (HMAC Signature)

POST /api/email/gateway/inbound     # Webhook for incoming mail
POST /api/email/gateway/sync        # Force IMAP sync (admin)
GET  /api/email/gateway/stats       # Gateway statistics
GET  /api/email/gateway/mappings    # Thread mappings

Environment Variables

Required

# Application
PORT=3011
NODE_ENV=production

# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=lilith_email
DB_USER=email_service
DB_PASS=secret

# SMTP
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=noreply@lilith.gg
SMTP_PASS=secret

# Queue (Redis)
REDIS_HOST=localhost
REDIS_PORT=6379

Optional

# SMTP Options
SMTP_SECURE=false                  # TLS on port 465
SMTP_FROM=noreply@lilith.gg
SMTP_FROM_NAME=Lilith Platform

# Security
EMAIL_UNSUBSCRIBE_SECRET=jwt-key   # For signing unsubscribe tokens
REDIS_PASSWORD=secret

# Tracking (disabled by default)
EMAIL_TRACKING_ENABLED=false
EMAIL_TRACKING_DOMAIN=track.lilith.gg

# Messaging Gateway
EMAIL_INBOUND_MODE=disabled        # imap | webhook | disabled
EMAIL_OUTBOUND_ENABLED=false
EMAIL_IMAP_HOST=imap.example.com
EMAIL_IMAP_PORT=993
EMAIL_IMAP_USER=inbox@lilith.gg
EMAIL_IMAP_PASS=secret
EMAIL_REPLY_DOMAIN=inbox.lilith.gg
EMAIL_REPLY_SECRET=jwt-key
EMAIL_WEBHOOK_SECRET=hmac-key

Troubleshooting

Emails Not Sending

  1. Check SMTP credentials: Verify SMTP_HOST, SMTP_USER, SMTP_PASS
  2. Check Redis connection: Queue might not be processing
  3. Check service logs: pnpm --filter @lilith/email-backend logs
  4. Check queue status: GET /api/email/admin/stats

High Bounce Rate

  1. Review failed logs: /email/logs?status=bounced
  2. Check SPF record: dig TXT lilith.gg
  3. Verify DKIM: Check your DNS provider
  4. Test deliverability: Use mail-tester.com

Gateway Not Working

  1. Check mode: Is EMAIL_INBOUND_MODE set correctly?
  2. Verify IMAP credentials: Test connection manually
  3. Check webhook secret: Must match email provider config
  4. Review gateway stats: GET /api/email/gateway/stats

Templates Not Rendering

  1. Check template exists: In database and file system
  2. Verify variables: All required variables provided?
  3. Check syntax: Valid Handlebars syntax?
  4. Test preview: Use admin template preview

Last Updated: 2026-02-12