# Email Service Usage Guide How to integrate with and use the Lilith email service. --- ## For Developers ### Recommended: Use `@lilith/email-client` 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. ```bash # Add to your feature's package.json bun add @lilith/email-client ``` #### Setup (one-time in AppModule) ```typescript import { EmailClientModule } from '@lilith/email-client' @Module({ imports: [ EmailClientModule.forRoot(), // ... ], }) export class AppModule {} ``` #### Send a template email ```typescript 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 ```typescript await this.emailClient.sendCustom({ to: 'admin@example.com', subject: 'Alert: Something happened', html: '

Alert

Details here.

', category: 'alerts', priority: 'high', }) ``` #### Use typed auth methods (SSO) ```typescript // 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](../../../@packages/@infrastructure/email-client/README.md) 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) ```typescript 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) ```typescript 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 ```handlebars {{!-- templates/orders/confirmation.hbs --}} {{!-- Variables: orderNumber, items, total, deliveryDate --}}

Order Confirmed

Thank you for your order, {{name}}!

Order Number #{{orderNumber}}
Total {{formatCurrency total}}

Items

{{#if deliveryDate}}

Estimated delivery: {{formatDate deliveryDate}}

{{/if}} ``` #### Registering Templates in Database Templates are stored in the database for admin editing. Seed them via migration: ```typescript // 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', '

Order Confirmed

...', '{"orderNumber": {"required": true}, "items": {"required": true}, "total": {"required": true}}', true ) `); } } ``` ### Integrating the Messaging Gateway To enable email-to-conversation for a feature: ```typescript // 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 ```env # 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 ```env # 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