Email Setup
Lektr includes a comprehensive email system for sending transactional and digest emails. This guide covers configuring SMTP settings, testing the connection, and understanding the email types.
Overviewβ
Lektr supports three types of emails:
| Email Type | Purpose | Trigger |
|---|---|---|
| Welcome | Greets new users | User registration |
| Password Reset | Account recovery link | Forgot password request |
| Daily Digest | Highlights due for review | Per-user schedule (timezone-aware) |
Configuration Methodsβ
You can configure email settings in two ways:
- Admin UI (Recommended) - Configure via the web interface
- Environment Variables - Set in
.envor Docker Compose
The Admin UI settings take precedence over environment variables.
Admin UI Configurationβ
- Log in as an admin user
- Navigate to Admin β Settings
- Scroll to the π§ Email Configuration section
SMTP Settingsβ
| Field | Description | Example |
|---|---|---|
| SMTP Host | Mail server hostname | smtp.gmail.com |
| SMTP Port | Server port | 587 (TLS) or 465 (SSL) |
| SMTP Username | Authentication username | your@email.com |
| SMTP Password | Authentication password | app-specific-password |
| Use TLS/SSL | Enable secure connection | Checked for port 465 |
Sender Informationβ
| Field | Description | Example |
|---|---|---|
| Sender Name | Display name in emails | Lektr |
| Sender Email | From address | noreply@yourdomain.com |
Testing Your Configurationβ
- Enter a test email address in the "Test Connection" field
- Click Send Test
- Check your inbox for the test email
If the test fails, check your SMTP credentials and ensure your email provider allows SMTP access.
Environment Variable Configurationβ
Add these to your .env file:
# SMTP Server Configuration
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-smtp-username
SMTP_PASS=your-smtp-password
SMTP_SECURE=false # Set to 'true' for port 465
# Sender Information
MAIL_FROM_NAME=Lektr
MAIL_FROM_EMAIL=noreply@yourdomain.com
# Application URL (used in email links)
APP_URL=https://lektr.yourdomain.com
Docker Compose Exampleβ
services:
lektr-api:
environment:
- SMTP_HOST=smtp.gmail.com
- SMTP_PORT=587
- SMTP_USER=${GMAIL_USER}
- SMTP_PASS=${GMAIL_APP_PASSWORD}
- SMTP_SECURE=false
- MAIL_FROM_NAME=Lektr
- MAIL_FROM_EMAIL=noreply@yourdomain.com
- APP_URL=https://lektr.yourdomain.com
Provider-Specific Guidesβ
Gmailβ
- Enable 2-Factor Authentication on your Google account
- Generate an App Password at https://myaccount.google.com/apppasswords
- Use these settings:
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your.email@gmail.com
SMTP_PASS=your-16-char-app-password
SMTP_SECURE=false
Never use your regular Gmail password. Always use an App Password.
SendGridβ
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=your-sendgrid-api-key
SMTP_SECURE=false
Mailgunβ
SMTP_HOST=smtp.mailgun.org
SMTP_PORT=587
SMTP_USER=postmaster@your-domain.mailgun.org
SMTP_PASS=your-mailgun-password
SMTP_SECURE=false
Amazon SESβ
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
SMTP_PORT=587
SMTP_USER=your-ses-access-key-id
SMTP_PASS=your-ses-secret-access-key
SMTP_SECURE=false
Daily Digestβ
The daily digest email sends users their highlights that are due for spaced repetition review.
How It Worksβ
- Scheduling: An hourly cron checks which users should receive their digest now, based on their preferred hour and timezone
- Selection Algorithm: Uses FSRS (Free Spaced Repetition Scheduler)
- First, selects highlights due for review
- If not enough, adds new/unreviewed highlights
- Falls back to random highlights for discovery
- Highlights per email: 5 highlights
- Duplicate prevention: A 20-hour dedup window prevents double-sends
User Preferencesβ
Each user can configure their digest via Settings β Notifications or the API:
| Setting | Options | Default |
|---|---|---|
| Enabled | On / Off | On |
| Frequency | Daily, Weekdays, Weekly (Monday) | Daily |
| Hour | 0β23 (in user's timezone) | 8 |
| Timezone | Any IANA timezone | UTC |
# Example: Update preferences via API
curl -X PATCH http://localhost:3001/api/v1/digest \
-H "Content-Type: application/json" \
-H "Cookie: auth_token=YOUR_TOKEN" \
-d '{"digestFrequency": "weekdays", "digestHour": 9, "digestTimezone": "America/New_York"}'
The DIGEST_CRON environment variable is no longer used. Scheduling is now per-user and timezone-aware. The server cron runs hourly and dispatches to each user at their preferred time.
Triggering a Test Digestβ
Admins can manually trigger digest emails for all enabled users:
Via Admin UI:
- Navigate to Admin β Settings
- Scroll to the π¬ Daily Digest section
- Click Send Digest and confirm in the modal
Via API:
curl -X POST http://localhost:3001/api/v1/admin/trigger-digest \
-H "Cookie: auth_token=YOUR_TOKEN"
Manual trigger bypasses timezone and frequency checks β it sends to all enabled users immediately.
Troubleshootingβ
Test email not receivedβ
- Check spam/junk folder
- Verify SMTP credentials
- Check API logs:
docker compose logs lektr-api | grep email
Connection timeoutsβ
- Ensure your firewall allows outbound connections on the SMTP port
- Try port 587 (STARTTLS) instead of 465 (SSL)
Authentication errorsβ
- Gmail: Ensure you're using an App Password, not your account password
- Check that special characters in passwords are properly escaped in
.env
Emails landing in spamβ
- Use a verified sender domain
- Set up SPF, DKIM, and DMARC records
- Use a reputable email provider (SendGrid, Mailgun, SES)
Architecture Detailsβ
For developers, here's how the email system works:
βββββββββββββββ ββββββββββββββββ βββββββββββββββ
β Email βββββββΆβ Job Queue βββββββΆβ SMTP β
β Service β β (PostgreSQL) β β Transport β
βββββββββββββββ ββββββββββββββββ βββββββββββββββ
β
βΌ
βββββββββββββββ
β React Email β
β Templates β
βββββββββββββββ
Componentsβ
| Component | File | Purpose |
|---|---|---|
| EmailService | src/services/email.ts | SMTP transport, config loading |
| JobQueueService | src/services/job-queue.ts | Reliable delivery with retries |
| DigestService | src/services/digest.ts | Daily digest scheduling & FSRS |
Email Templatesβ
Located in lektr-api/src/emails/:
welcome.tsx- New user welcomepassword-reset.tsx- Password recoverydaily-digest.tsx- Daily highlights digest
Templates use React Email for modern, responsive HTML emails.
API Endpointsβ
Admin Endpointsβ
| Endpoint | Method | Purpose |
|---|---|---|
/api/v1/admin/email-settings | GET | Get current config |
/api/v1/admin/email-settings | PUT | Update config |
/api/v1/admin/email-settings/test | POST | Send test email |
/api/v1/admin/trigger-digest | POST | Trigger digest emails |
/api/v1/admin/job-queue/status | GET | Queue status |
User Endpointsβ
| Endpoint | Method | Purpose |
|---|---|---|
/api/v1/digest | GET | Get digest preferences |
/api/v1/digest | PATCH | Update digest preferences |
Example: Update Email Settingsβ
curl -X PUT http://localhost:3001/api/v1/admin/email-settings \
-H "Content-Type: application/json" \
-H "Cookie: auth_token=YOUR_TOKEN" \
-d '{
"smtp_host": "smtp.gmail.com",
"smtp_port": "587",
"smtp_user": "your@email.com",
"smtp_pass": "app-password",
"smtp_secure": "false",
"mail_from_name": "Lektr",
"mail_from_email": "noreply@example.com"
}'
Example: Send Test Emailβ
curl -X POST http://localhost:3001/api/v1/admin/email-settings/test \
-H "Content-Type: application/json" \
-H "Cookie: auth_token=YOUR_TOKEN" \
-d '{"email": "test@example.com"}'