Skip to main content

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 TypePurposeTrigger
WelcomeGreets new usersUser registration
Password ResetAccount recovery linkForgot password request
Daily DigestHighlights due for reviewPer-user schedule (timezone-aware)

Configuration Methods​

You can configure email settings in two ways:

  1. Admin UI (Recommended) - Configure via the web interface
  2. Environment Variables - Set in .env or Docker Compose

The Admin UI settings take precedence over environment variables.


Admin UI Configuration​

  1. Log in as an admin user
  2. Navigate to Admin β†’ Settings
  3. Scroll to the πŸ“§ Email Configuration section

SMTP Settings​

FieldDescriptionExample
SMTP HostMail server hostnamesmtp.gmail.com
SMTP PortServer port587 (TLS) or 465 (SSL)
SMTP UsernameAuthentication usernameyour@email.com
SMTP PasswordAuthentication passwordapp-specific-password
Use TLS/SSLEnable secure connectionChecked for port 465

Sender Information​

FieldDescriptionExample
Sender NameDisplay name in emailsLektr
Sender EmailFrom addressnoreply@yourdomain.com

Testing Your Configuration​

  1. Enter a test email address in the "Test Connection" field
  2. Click Send Test
  3. Check your inbox for the test email
tip

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​

  1. Enable 2-Factor Authentication on your Google account
  2. Generate an App Password at https://myaccount.google.com/apppasswords
  3. 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
caution

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​

  1. Scheduling: An hourly cron checks which users should receive their digest now, based on their preferred hour and timezone
  2. 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
  3. Highlights per email: 5 highlights
  4. Duplicate prevention: A 20-hour dedup window prevents double-sends

User Preferences​

Each user can configure their digest via Settings β†’ Notifications or the API:

SettingOptionsDefault
EnabledOn / OffOn
FrequencyDaily, Weekdays, Weekly (Monday)Daily
Hour0–23 (in user's timezone)8
TimezoneAny IANA timezoneUTC
# 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"}'
info

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:

  1. Navigate to Admin β†’ Settings
  2. Scroll to the πŸ“¬ Daily Digest section
  3. 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"
caution

Manual trigger bypasses timezone and frequency checks β€” it sends to all enabled users immediately.


Troubleshooting​

Test email not received​

  1. Check spam/junk folder
  2. Verify SMTP credentials
  3. 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​

ComponentFilePurpose
EmailServicesrc/services/email.tsSMTP transport, config loading
JobQueueServicesrc/services/job-queue.tsReliable delivery with retries
DigestServicesrc/services/digest.tsDaily digest scheduling & FSRS

Email Templates​

Located in lektr-api/src/emails/:

  • welcome.tsx - New user welcome
  • password-reset.tsx - Password recovery
  • daily-digest.tsx - Daily highlights digest

Templates use React Email for modern, responsive HTML emails.


API Endpoints​

Admin Endpoints​

EndpointMethodPurpose
/api/v1/admin/email-settingsGETGet current config
/api/v1/admin/email-settingsPUTUpdate config
/api/v1/admin/email-settings/testPOSTSend test email
/api/v1/admin/trigger-digestPOSTTrigger digest emails
/api/v1/admin/job-queue/statusGETQueue status

User Endpoints​

EndpointMethodPurpose
/api/v1/digestGETGet digest preferences
/api/v1/digestPATCHUpdate 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"}'