27 KiB

Email and SMTP Issues

This guide covers email sending, SMTP configuration, and template-related problems in Changemaker Lite V2.

Overview

Email System Architecture

Changemaker Lite V2 has dual email systems:

  1. Transactional Emails (BullMQ + Nodemailer)

    • Campaign advocacy emails
    • Shift confirmation emails
    • Response verification emails
    • System notifications
  2. Newsletter Emails (Listmonk)

    • Marketing campaigns
    • Newsletter broadcasts
    • Subscriber management

Email Flow

User Action → Email Service → BullMQ Queue → Worker → SMTP Server → Recipient

Key Components

  • BullMQ - Job queue for async email sending
  • Nodemailer - SMTP client library
  • Redis - Queue backend
  • MailHog - Development email capture (test mode)
  • Listmonk - Newsletter platform (optional)

SMTP Configuration

Connection Refused

Severity: 🔴 Critical

Symptoms

API logs:

Error: Connection timeout
Error: connect ECONNREFUSED smtp.gmail.com:587
Error: Invalid login: 535-5.7.8 Username and Password not accepted

Emails not sending.

Common Causes

  1. Wrong SMTP host - Incorrect hostname
  2. Port blocked - Firewall blocking port 587/465
  3. Wrong credentials - Invalid username/password
  4. TLS/SSL mismatch - Wrong secure setting

Solutions

Solution 1: Test SMTP connection

# Test with telnet
telnet smtp.gmail.com 587

# Should show:
# 220 smtp.gmail.com ESMTP ...

# Or test with openssl (for SSL)
openssl s_client -connect smtp.gmail.com:465

Solution 2: Verify SMTP configuration

In .env:

# Gmail example (requires app password)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false  # false for STARTTLS on 587, true for SSL on 465
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password  # NOT regular password
SMTP_FROM=your-email@gmail.com

# Office365 example
SMTP_HOST=smtp.office365.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@outlook.com
SMTP_PASS=your-password
SMTP_FROM=your-email@outlook.com

# SendGrid example
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=apikey  # Literally "apikey"
SMTP_PASS=your-sendgrid-api-key
SMTP_FROM=your-verified-sender@example.com

Solution 3: Use test mode

# In .env
EMAIL_TEST_MODE=true

# Restart API
docker compose restart api

# All emails now sent to MailHog
# View at http://localhost:8025

Solution 4: Test email sending

# Send test email via API
curl -X POST http://localhost:4000/api/test-email \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "to": "test@example.com",
    "subject": "Test Email",
    "text": "This is a test email from Changemaker Lite"
  }'

# Check API logs
docker compose logs api | grep -i "email\|smtp"

Solution 5: Gmail app password

For Gmail (required if 2FA enabled):

  1. Go to https://myaccount.google.com/apppasswords
  2. Select app: Mail
  3. Select device: Other (Changemaker Lite)
  4. Click Generate
  5. Copy 16-character password
  6. Use in SMTP_PASS (no spaces)

Prevention

  • Test mode for dev - Use MailHog locally
  • Secure credentials - Use app passwords, not real passwords
  • Environment-specific - Different SMTP per environment
  • Health checks - Test SMTP on API startup

Authentication Failed

Severity: 🔴 Critical

Symptoms

Error: Invalid login: 535-5.7.8 Username and Password not accepted
Error: 535 Authentication failed

Common Causes

  1. Wrong password - Incorrect password
  2. 2FA enabled - Need app password
  3. Less secure apps - Gmail blocking
  4. Account locked - Too many failed attempts

Solutions

Solution 1: Verify credentials

# Check .env
cat .env | grep SMTP_

# Test login manually (if possible)
# Gmail doesn't allow this, but some SMTP servers do

Solution 2: Enable less secure apps (Gmail)

⚠️ Not recommended. Use app password instead.

  1. Go to https://myaccount.google.com/lesssecureapps
  2. Turn on "Allow less secure apps"

Solution 3: Check account status

  1. Try logging into email account via web
  2. Check for security alerts
  3. Verify account not locked

Solution 4: Use OAuth2 (advanced)

For production Gmail:

// In email.service.ts
const transporter = nodemailer.createTransporter({
  service: 'gmail',
  auth: {
    type: 'OAuth2',
    user: process.env.SMTP_USER,
    clientId: process.env.GMAIL_CLIENT_ID,
    clientSecret: process.env.GMAIL_CLIENT_SECRET,
    refreshToken: process.env.GMAIL_REFRESH_TOKEN
  }
});

Prevention

  • App passwords - Always use app-specific passwords
  • Test credentials - Verify before deploying
  • Monitor failures - Alert on auth failures
  • Backup SMTP - Configure fallback SMTP server

Invalid Credentials

Severity: 🔴 Critical

Symptoms

Error: Invalid SMTP credentials
Error: Username and Password not accepted

Solutions

See "Authentication Failed" section above.


Port Blocked

Severity: 🟠 High

Symptoms

Error: connect ETIMEDOUT smtp.gmail.com:587
Error: Connection timeout

Connection attempt hangs, then times out after 30+ seconds.

Common Causes

  1. Firewall blocking - Network firewall blocking port
  2. ISP blocking - ISP blocks port 25/587
  3. Docker network - Container can't reach external SMTP

Solutions

Solution 1: Test port access

# From API container
docker compose exec api telnet smtp.gmail.com 587

# If timeout, port is blocked

Solution 2: Try alternative port

# Try port 465 (SSL) instead of 587 (STARTTLS)
SMTP_PORT=465
SMTP_SECURE=true

# Or try port 2525 (some providers)
SMTP_PORT=2525
SMTP_SECURE=false

Solution 3: Check Docker network

# Test external connectivity
docker compose exec api ping -c 3 smtp.gmail.com

# Test DNS resolution
docker compose exec api nslookup smtp.gmail.com

# If fails, Docker network issue

Solution 4: Use SMTP relay

If ISP blocks SMTP, use relay service:

  • SendGrid
  • Mailgun
  • Amazon SES
  • Postmark

Solution 5: VPN or proxy

As last resort, route SMTP through VPN/proxy.

Prevention

  • Use relay services - More reliable than direct SMTP
  • Multiple ports - Try 587, 465, 2525
  • Test on deploy - Verify SMTP works in production
  • Documentation - Document network requirements

Template Issues

Template Not Found

Severity: 🟠 High

Symptoms

API logs:

Error: Email template not found: campaign-email
Error: ENOENT: no such file or directory, open 'templates/campaign-email.html'

Common Causes

  1. Template file missing - File doesn't exist
  2. Wrong template name - Typo in name
  3. Wrong directory - Looking in wrong path
  4. Deleted template - Template was removed

Solutions

Solution 1: List available templates

# List template files
docker compose exec api ls -la templates/

# Should show:
# campaign-email.html
# shift-confirmation.html
# verification-email.html
# response-verification.html

Solution 2: Create missing template

# Create template file
docker compose exec api sh -c 'cat > templates/my-template.html << EOF
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>{{title}}</title>
</head>
<body>
  <h1>Hello {{name}}</h1>
  <p>{{message}}</p>
</body>
</html>
EOF'

Solution 3: Use email template system

Navigate to /app/email-templates:

  1. Click "Create Template"
  2. Fill in details
  3. Design template
  4. Save (creates file + DB record)

Solution 4: Check template name

// In code, template name must match filename (without .html)
await emailService.sendEmail({
  to: email,
  subject: 'Campaign Email',
  template: 'campaign-email',  // Looks for templates/campaign-email.html
  variables: { ... }
});

Solution 5: Verify template path

In api/src/services/email.service.ts:

const templatePath = path.join(__dirname, '../../templates', `${template}.html`);
// Resolves to: api/templates/campaign-email.html

Prevention

  • Seed templates - Include default templates in seed
  • Template management - Use admin UI to manage
  • Version control - Keep templates in git
  • Validation - Check template exists before sending

Variable Not Replaced

Severity: 🟡 Medium

Symptoms

Email received with unreplaced placeholders:

Hello {{name}},

Your campaign {{campaignName}} is ready.

Common Causes

  1. Variable not provided - Missing from variables object
  2. Typo in variable name - Mismatch between template and code
  3. Wrong delimiter - Using ${} instead of {{}}
  4. Escaping issue - HTML entities interfering

Solutions

Solution 1: List template variables

# Find all variables in template
docker compose exec api grep -o '{{[^}]*}}' templates/campaign-email.html

# Shows:
# {{name}}
# {{campaignName}}
# {{campaignUrl}}

Solution 2: Provide all variables

await emailService.sendEmail({
  to: email,
  subject: 'Campaign Ready',
  template: 'campaign-email',
  variables: {
    name: user.name,  // Must provide ALL variables in template
    campaignName: campaign.name,
    campaignUrl: `${process.env.PUBLIC_URL}/campaigns/${campaign.id}`
  }
});

Solution 3: Check variable delimiter

<!-- Correct (Handlebars-style) -->
<h1>Hello {{name}}</h1>
<p>Your campaign {{campaignName}} is ready.</p>

<!-- Wrong -->
<h1>Hello ${name}</h1>  <!-- JavaScript template literal -->
<p>Your campaign {campaignName} is ready.</p>  <!-- Single braces -->

Solution 4: Test template rendering

# Test template rendering
curl -X POST http://localhost:4000/api/test-template \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "template": "campaign-email",
    "variables": {
      "name": "John",
      "campaignName": "Save the Planet",
      "campaignUrl": "https://example.com/campaigns/123"
    }
  }'

# Returns rendered HTML

Solution 5: Use default values

<!-- In template, provide fallback -->
<h1>Hello {{name || "Friend"}}</h1>

Or in code:

const variables = {
  name: user.name || 'Friend',
  campaignName: campaign.name || 'Campaign',
  campaignUrl: campaignUrl || '#'
};

Prevention

  • Template validation - Check all variables exist
  • TypeScript types - Type template variables
  • Default values - Always provide defaults
  • Testing - Test all templates with sample data

Syntax Errors

Severity: 🟠 High

Symptoms

Error: Parse error in template at line 15
Error: Unexpected token in template

Email fails to send.

Common Causes

  1. Invalid HTML - Malformed HTML
  2. Unclosed tags - Missing closing tags
  3. Special characters - Unescaped < > &
  4. Handlebars syntax - Invalid {{}} usage

Solutions

Solution 1: Validate HTML

# Use HTML validator
# Copy template content to https://validator.w3.org/nu/

# Or validate locally
docker compose exec api npx html-validate templates/campaign-email.html

Solution 2: Check common errors

<!-- Unclosed tag -->
<div>Content here
<!-- Should be: -->
<div>Content here</div>

<!-- Unescaped characters -->
Price: $50 < $100
<!-- Should be: -->
Price: $50 &lt; $100

<!-- Invalid Handlebars -->
{{if name}}  <!-- No "if" helper by default -->
<!-- Should be: -->
{{#if name}}...{{/if}}  <!-- Or don't use if -->

Solution 3: Escape HTML

// In email.service.ts
import handlebars from 'handlebars';

// Register escape helper
handlebars.registerHelper('escape', (str) => {
  return handlebars.escapeExpression(str);
});

// In template
<p>Message: {{escape message}}</p>

Solution 4: Test template compilation

// Test if template compiles
import handlebars from 'handlebars';
import fs from 'fs';

const templateSource = fs.readFileSync('templates/campaign-email.html', 'utf8');
try {
  const template = handlebars.compile(templateSource);
  console.log('Template compiles successfully');
} catch (error) {
  console.error('Template error:', error.message);
}

Prevention

  • HTML validation - Validate before saving
  • Linting - Use HTML linter in editor
  • Simple templates - Keep templates simple
  • Testing - Test rendering before deploying

Queue Issues

Queue Stuck

Severity: 🟠 High

Symptoms

Emails queued but not sending. Queue shows jobs but no progress.

Solutions

Solution 1: Check queue status

# View queue stats
curl http://localhost:4000/api/influence/email-queue/stats \
  -H "Authorization: Bearer YOUR_TOKEN"

# Shows:
# {
#   "waiting": 50,
#   "active": 0,  # Should be > 0 if processing
#   "completed": 1000,
#   "failed": 5
# }

Solution 2: Check worker is running

# Worker should log processing
docker compose logs api | grep -i "email worker\|processing email"

# Should show:
# Email worker started
# Processing email job for campaign: abc-123

Solution 3: Restart worker

# Restart API (restarts worker)
docker compose restart api

# Check worker started
docker compose logs api | grep "Email worker started"

Solution 4: Check Redis

# Test Redis connection
docker compose exec redis redis-cli -a YOUR_REDIS_PASSWORD ping

# Check queue keys
docker compose exec redis redis-cli -a YOUR_REDIS_PASSWORD keys "bull:email-queue:*"

Solution 5: Process stuck jobs

# Retry failed jobs
curl -X POST http://localhost:4000/api/influence/email-queue/retry-failed \
  -H "Authorization: Bearer YOUR_TOKEN"

# Clean old jobs
curl -X POST http://localhost:4000/api/influence/email-queue/clean \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"status": "completed", "grace": 86400000}'  # Clean completed > 1 day

Prevention

  • Health checks - Monitor worker health
  • Auto-restart - Restart worker if stuck
  • Alerting - Alert if queue backed up
  • Dead letter queue - Move repeatedly failed jobs

Jobs Failing

Severity: 🟠 High

Symptoms

High failed job count. Emails not reaching recipients.

Solutions

Solution 1: View failed jobs

# Get failed job details
curl http://localhost:4000/api/influence/email-queue/failed \
  -H "Authorization: Bearer YOUR_TOKEN"

# Shows:
# [
#   {
#     "id": "123",
#     "data": { "to": "user@example.com", "subject": "..." },
#     "failedReason": "SMTP connection failed",
#     "attemptsMade": 3
#   }
# ]

Solution 2: Check error patterns

# Common failure reasons
docker compose logs api | grep "Email failed" | sort | uniq -c

# Example output:
#  25 Email failed: Invalid email address
#  10 Email failed: SMTP connection refused
#   3 Email failed: Recipient mailbox full

Solution 3: Retry with fixes

# Fix SMTP config if needed
# Then retry failed jobs
curl -X POST http://localhost:4000/api/influence/email-queue/retry-failed \
  -H "Authorization: Bearer YOUR_TOKEN"

Solution 4: Manual intervention

For repeatedly failing emails:

  1. Check email address validity
  2. Verify SMTP configuration
  3. Test with different recipient
  4. Check if recipient's mailbox full

Prevention

  • Retry logic - Auto-retry with exponential backoff
  • Email validation - Validate before queuing
  • Error categorization - Permanent vs transient failures
  • Bounce handling - Handle bounce notifications

Delivery Issues

Emails Not Arriving

Severity: 🔴 Critical

Symptoms

Emails sent successfully (no errors) but not received.

Common Causes

  1. Spam folder - Filtered to spam
  2. Email delay - Taking long to deliver
  3. Email blocking - Recipient server blocking
  4. Wrong address - Typo in email address

Solutions

Solution 1: Check spam folder

  1. Check spam/junk folder
  2. Check promotions tab (Gmail)
  3. Mark as "Not Spam" to whitelist

Solution 2: Check email logs

# Verify email was sent
docker compose logs api | grep "Email sent"

# Should show:
# Email sent to user@example.com: Campaign Email

Solution 3: Use MailHog to test

# In .env
EMAIL_TEST_MODE=true

# Restart API
docker compose restart api

# Send test email
curl -X POST http://localhost:4000/api/test-email \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"to": "test@example.com", "subject": "Test", "text": "Test"}'

# Check MailHog
# http://localhost:8025

# If appears in MailHog, SMTP working
# If not appearing in real inbox, delivery issue

Solution 4: Check email headers

In MailHog or received email:

  1. View full headers
  2. Check "Received" path
  3. Look for spam scores
  4. Check SPF/DKIM/DMARC status

Solution 5: Test with different address

# Try sending to different email provider
# Gmail vs Outlook vs Yahoo
# If some work and others don't, specific provider blocking

Prevention

  • Email authentication - SPF, DKIM, DMARC
  • Reputation management - Maintain good sender reputation
  • Bounce handling - Monitor bounces
  • Testing - Regular delivery tests

Marked as Spam

Severity: 🟠 High

Symptoms

Emails consistently go to spam folder.

Solutions

Solution 1: Configure SPF

Add TXT record to DNS:

v=spf1 include:_spf.google.com ~all

Or for SendGrid:

v=spf1 include:sendgrid.net ~all

Solution 2: Configure DKIM

  1. Generate DKIM keys (via email provider)
  2. Add DKIM TXT record to DNS
  3. Enable DKIM signing in SMTP settings

Solution 3: Configure DMARC

Add TXT record to DNS:

v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com

Solution 4: Improve email content

  • Use plain text version alongside HTML
  • Avoid spam trigger words ("FREE", "CLICK HERE", "ACT NOW")
  • Proper from/reply-to addresses
  • Unsubscribe link
  • Physical address in footer

Solution 5: Warm up IP

If using dedicated IP:

  1. Start with low volume
  2. Gradually increase over weeks
  3. Monitor reputation scores

Prevention

  • Email authentication - SPF, DKIM, DMARC mandatory
  • Content quality - Professional, non-spammy content
  • Reputation monitoring - Monitor sender scores
  • Engagement - High engagement = good reputation

Bounce Errors

Severity: 🟡 Medium

Symptoms

Email bounced: user@example.com
554 Recipient address rejected: User unknown

Common Causes

  1. Invalid address - Email doesn't exist
  2. Full mailbox - Recipient mailbox full
  3. Temporary failure - Server temporarily unavailable
  4. Blocked sender - Your domain/IP blocked

Solutions

Solution 1: Categorize bounces

Hard bounces (permanent):

  • User unknown
  • Domain doesn't exist
  • Invalid address format

Soft bounces (temporary):

  • Mailbox full
  • Server temporarily unavailable
  • Message too large

Solution 2: Handle hard bounces

# Remove hard bounce addresses
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
  -c "UPDATE \"User\" SET \"emailBounced\" = true
      WHERE email = 'bounced@example.com';"

# Don't send to bounced addresses

Solution 3: Retry soft bounces

# Retry soft bounces after delay
curl -X POST http://localhost:4000/api/influence/email-queue/retry-failed \
  -H "Authorization: Bearer YOUR_TOKEN"

Solution 4: Validate emails before sending

import validator from 'validator';

const isValidEmail = validator.isEmail(email);
if (!isValidEmail) {
  throw new Error('Invalid email address');
}

Prevention

  • Email validation - Validate before saving
  • Bounce tracking - Track bounces per address
  • Automatic removal - Don't send to bounced addresses
  • Double opt-in - Confirm email addresses work

Listmonk Integration

API Connection Failed

Severity: 🟠 High

Symptoms

Error: Failed to connect to Listmonk API
Error: ECONNREFUSED localhost:9001

Solutions

Solution 1: Check Listmonk is running

docker compose ps listmonk

# Should show "Up"
# If not:
docker compose up -d listmonk

Solution 2: Verify API credentials

# Check .env
cat .env | grep LISTMONK_

# Required:
LISTMONK_URL=http://listmonk:9001
LISTMONK_ADMIN_USER=admin
LISTMONK_ADMIN_PASSWORD=password

Solution 3: Test API connection

# From API container
docker compose exec api curl -u admin:password http://listmonk:9001/api/health

# Should return:
# {"data": "OK"}

Solution 4: Check Docker network

# Both on same network?
docker inspect changemaker-lite-api-1 | grep NetworkMode
docker inspect changemaker-lite-listmonk-1 | grep NetworkMode

# Should both show "changemaker-lite"

Prevention

  • Health checks - Verify Listmonk health on API startup
  • Proper credentials - Use API user (not web admin)
  • Network config - Ensure same Docker network
  • Error handling - Graceful degradation if Listmonk down

Sync Errors

Severity: 🟡 Medium

Symptoms

Error: Failed to sync subscribers to Listmonk
Error: 400 Bad Request: Invalid email format

Solutions

Solution 1: Check sync status

Navigate to /app/listmonk:

  • View sync statistics
  • See last sync time
  • Check error count

Solution 2: View sync logs

docker compose logs api | grep -i "listmonk\|sync"

# Shows:
# Syncing 150 participants to Listmonk
# Created list: Campaign Participants
# Added 145 subscribers, 5 failed

Solution 3: Manual sync

# Trigger manual sync
curl -X POST http://localhost:4000/api/listmonk/sync \
  -H "Authorization: Bearer YOUR_TOKEN"

Solution 4: Check subscriber data

# View failed subscribers
docker compose logs api | grep "Failed to add subscriber"

# Common issues:
# - Invalid email format
# - Email already exists
# - Missing required fields

Prevention

  • Data validation - Validate before sync
  • Duplicate handling - Handle existing subscribers
  • Error logging - Log sync errors
  • Regular syncs - Automated periodic syncs

Performance Issues

Slow Email Sending

Severity: 🟡 Medium

Symptoms

Sending emails takes several seconds each. Bulk sends very slow.

Solutions

Solution 1: Use queue system

# Don't send synchronously
# Queue emails instead
curl -X POST http://localhost:4000/api/influence/campaigns/CAMPAIGN_ID/send-bulk \
  -H "Authorization: Bearer YOUR_TOKEN"

# Processes in background via queue

Solution 2: Increase worker concurrency

In api/src/services/email-queue.service.ts:

const worker = new Worker('email-queue', processor, {
  concurrency: 5,  // Process 5 emails at a time (default: 1)
  limiter: {
    max: 50,  // Max 50 emails per second
    duration: 1000
  }
});

Solution 3: Use batch sending

For transactional email services:

// Some SMTP services support batch sending
// Send 100 emails in single API call instead of 100 separate calls

Solution 4: Check SMTP performance

# Test SMTP connection speed
time curl -X POST http://localhost:4000/api/test-email \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"to": "test@example.com", "subject": "Test", "text": "Test"}'

# Should complete in < 2 seconds
# If > 5 seconds, SMTP server slow

Solution 5: Use email service

For high volume, use transactional email service:

  • SendGrid
  • Mailgun
  • Amazon SES
  • Postmark

Faster and more reliable than SMTP.

Prevention

  • Queue system - Never send synchronously
  • Worker concurrency - Process multiple at once
  • Email service - Use dedicated email service
  • Rate limiting - Respect provider limits

Queue Backlog

Severity: 🟡 Medium

Symptoms

Thousands of emails waiting in queue. Taking hours to process.

Solutions

Solution 1: Increase worker count

Start multiple API instances:

# In docker-compose.yml
api:
  deploy:
    replicas: 3  # 3 API instances

Each instance runs its own worker.

Solution 2: Increase concurrency

See "Slow Email Sending" section above.

Solution 3: Pause new emails

# Pause queue
curl -X POST http://localhost:4000/api/influence/email-queue/pause \
  -H "Authorization: Bearer YOUR_TOKEN"

# Process backlog
# Resume when caught up
curl -X POST http://localhost:4000/api/influence/email-queue/resume \
  -H "Authorization: Bearer YOUR_TOKEN"

Solution 4: Clean old jobs

# Remove completed jobs
curl -X POST http://localhost:4000/api/influence/email-queue/clean \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"status": "completed", "grace": 3600000}'  # Older than 1 hour

Prevention

  • Monitor queue size - Alert when > 1000 waiting
  • Rate limiting - Don't queue faster than can process
  • Capacity planning - Size workers for expected load
  • Cleanup jobs - Regular cleanup of old jobs

Useful Commands

Testing Email

# Send test email
curl -X POST http://localhost:4000/api/test-email \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "to": "test@example.com",
    "subject": "Test Email",
    "text": "This is a test email",
    "html": "<h1>Test Email</h1><p>This is a test email</p>"
  }'

# Test with template
curl -X POST http://localhost:4000/api/test-template-email \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "to": "test@example.com",
    "subject": "Test Template",
    "template": "campaign-email",
    "variables": {
      "name": "Test User",
      "campaignName": "Test Campaign"
    }
  }'

Queue Management

# Get queue stats
curl http://localhost:4000/api/influence/email-queue/stats \
  -H "Authorization: Bearer YOUR_TOKEN"

# Pause queue
curl -X POST http://localhost:4000/api/influence/email-queue/pause \
  -H "Authorization: Bearer YOUR_TOKEN"

# Resume queue
curl -X POST http://localhost:4000/api/influence/email-queue/resume \
  -H "Authorization: Bearer YOUR_TOKEN"

# Retry failed
curl -X POST http://localhost:4000/api/influence/email-queue/retry-failed \
  -H "Authorization: Bearer YOUR_TOKEN"

# Clean completed
curl -X POST http://localhost:4000/api/influence/email-queue/clean \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"status": "completed", "grace": 86400000}'

Listmonk Operations

# Test Listmonk connection
curl -u admin:password http://localhost:9001/api/health

# Get lists
curl -u admin:password http://localhost:9001/api/lists

# Sync subscribers
curl -X POST http://localhost:4000/api/listmonk/sync \
  -H "Authorization: Bearer YOUR_TOKEN"

# Get sync status
curl http://localhost:4000/api/listmonk/status \
  -H "Authorization: Bearer YOUR_TOKEN"

Email Documentation

Other Troubleshooting

External Resources


Last Updated: February 2026 Version: V2.0 Status: Complete