Email Campaigner — Scope & User Guide

Version: 1.0 Date: 2026-04-15 App URL: http://localhost:8098/ API URL: http://localhost:3002/

1. Project overview

Email Campaigner is a self-hosted, web-based email marketing platform. It allows teams to manage contacts, build email templates, send bulk campaigns, track opens/clicks, and send one-off transactional emails — all from a single dashboard.

Key capabilities:

2. Technology stack

LayerTechnology
FrontendReact 18 + TypeScript, Vite, Tailwind CSS, React Router
APIExpress (Node.js / TypeScript)
DatabasePostgreSQL
AuthSupabase (GoTrue) — JWT-based sessions
GatewayKong (API gateway)
EmailNodemailer over SMTP (MailHog in dev)
Template engineGrapesJS (visual), MJML (code), Handlebars (merge fields)
InfrastructureDocker Compose (all services)

3. Feature scope

In scope (current)

FeatureStatus
Authentication (login / sign-up / sign-out / forgot password)Implemented
Dashboard with KPI cards and recent campaignsImplemented
Campaign CRUD (create, edit, view, delete)Implemented
Campaign sending with batch processingImplemented
Test email sendsImplemented
Contact management (CRUD + CSV import)Implemented
List management (create, edit, assign contacts)Implemented
Template editor (GrapesJS visual + MJML code)Implemented
Open / click / bounce / unsubscribe trackingImplemented
Analytics dashboard per campaignImplemented
Transactional email sendingImplemented
App settings (SMTP, sender defaults)Implemented
User management (admin-only)Implemented
Dark / light theme toggleImplemented
AI enrichment (LLM per recipient, human review, enriched send)Implemented
Reply mailbox (Microsoft 365 IMAP, reply-to-unsubscribe, forwarding)Implemented

Out of scope (future)

4. Screen-by-screen user guide

4.1 Login

Login page showing welcome card, email and password fields, and Sign In button
Login page

URL: /login

What you see:

How to use:

  1. Enter your email address and password
  2. Click Sign In
  3. You will be redirected to the Dashboard on success
  4. If you don’t have an account, click Sign up to register

Forgot password: Click Forgot password?, enter your email, then Send reset link. Open the link in the message (it opens /reset-password), set a new password, and you are signed in to the Dashboard. Add the exact redirect URL (e.g. http://localhost:8098/reset-password) to Supabase Auth redirect allow-list; the app uses the current browser origin for redirectTo.

4.2 Dashboard

Dashboard with KPI cards, send activity area, and recent campaigns list
Dashboard

URL: / (root, after login)

What you see:

Sidebar (left): Persistent navigation with links to all sections:

Main area:

How to use:

4.3 Campaigns

Campaigns list with search and action buttons per row
Campaigns list

URL: /campaigns

What you see:

How to use:

  1. Click + New Campaign to start a new campaign
  2. Fill in the campaign name, subject, sender details, pick a list and template
  3. Use the search bar to find campaigns by name
  4. Use the eye, pencil, paper plane, and trash icons to view, edit, send, or delete

4.4 Campaign detail

Campaign detail with stats, campaign info, and recipients section
Campaign detail

URL: /campaigns/:id

What you see:

How to use:

  1. Review campaign stats after sending
  2. Click Edit to modify the campaign (drafts)
  3. Click Send Campaign to deliver to subscribed contacts on the list

4.5 Contacts

Contacts list with import, add, search, and rows per contact
Contacts

URL: /contacts

What you see:

How to use:

  1. Add contacts manually or via CSV import
  2. Search by name or email
  3. Open a contact for full detail and campaign history
  4. Unsubscribed contacts are excluded from future campaign sends

4.6 Lists

Grid of mailing list cards with contact counts and actions
Lists

URL: /lists

What you see:

How to use:

  1. Create lists and assign contacts
  2. Select a list as the audience when creating a campaign
  3. Only subscribed contacts on the list receive the campaign

4.7 Templates

Template cards with edit and delete actions
Templates

URL: /templates

What you see:

How to use:

  1. Create templates in GrapesJS or MJML
  2. Use Handlebars merge fields (e.g. {{first_name}})
  3. Attach a template when creating a campaign

4.8 Analytics

Analytics page before selecting a campaign
Analytics — empty state

URL: /analytics

Before selecting a campaign: filter field, campaign dropdown, message “Select a sent campaign to view analytics”.

Analytics with stat cards and events over time chart
Analytics — with data

After selecting a campaign: Sent, Unique Opens, Unique Clicks, Bounces; Events Over Time chart; legend (Opens, Clicks, Sent).

How to use:

  1. Choose a sent campaign from the dropdown
  2. Filter by campaign name
  3. Review rates and the timeline

4.9 Transactional emails

Transactional emails list with Send Email button
Transactional emails

URL: /transactional

What you see: “Transactional Emails” heading, Send Email button, history rows with subject, recipient, time, status (sent / queued).

How to use:

  1. Click Send Email for one-off sends
  2. Optional Handlebars variables for personalisation
  3. Use for welcome messages, receipts, and other non-campaign mail

4.10 Settings

Settings page with SMTP and sender defaults
Settings

URL: /settings

What you see: Save Settings; SMTP Host, Port, Username, Password; dev note about MailHog; Default From Name and Email.

How to use:

  1. Configure SMTP for production
  2. Set default From name and email
  3. Click Save Settings
  4. In development, view caught mail at http://localhost:8025

4.11 Users (admin)

Admin access required message on users page
Users — admin required (non-admin)

URL: /users

What you see (non-admin): shield icon, “Admin access required”, and explanatory text.

Admin features: list users, invite users, change roles, delete users.

How to use: Restricted to the admin role; regular users must contact an administrator.

5. Database schema

TablePurpose
contactsStores email addresses, names, custom fields, subscription status
listsNamed mailing lists
list_contactsMany-to-many relationship between lists and contacts
templatesEmail templates (GrapesJS JSON + compiled HTML)
campaignsCampaign metadata, status, list/template associations
campaign_recipientsPer-recipient send state, tracking tokens, delivery timestamps
email_eventsEvent log (sent, opened, clicked, bounced, unsubscribed)
tracked_linksRewritten URLs for click tracking per campaign
tracked_link_clicksIndividual click records with user agent and IP
transactional_emailsOne-off transactional send log
app_settingsKey-value application settings (SMTP, sender defaults)
profilesUser profiles with roles (admin, user)
campaign_settingsAI tone, instructions, send-from, reply-unsubscribe, forward/BCC
campaign_enriched_emailsOriginal vs enriched copy, review status, Message-ID
inbox_messagesInbound reply log and classification

6. API endpoints

MethodPathDescription
GET/healthHealth check
GET/POST/PUT/DELETE/api/campaignsCampaign CRUD
POST/api/campaigns/:id/sendSend a campaign
POST/api/campaigns/:id/testSend a test email
GET/api/campaigns/:id/recipientsList campaign recipients
GET/POST/PUT/DELETE/api/contactsContact CRUD
POST/api/contacts/importCSV import
GET/POST/PUT/DELETE/api/listsList CRUD
POST/api/lists/:id/contactsAdd contacts to a list
GET/POST/PUT/DELETE/api/templatesTemplate CRUD
POST/api/templates/compileCompile MJML to HTML
POST/api/transactionalSend a transactional email
GET/api/transactionalList transactional email history
GET/api/analyticsOverview analytics
GET/api/analytics/:campaignIdPer-campaign analytics
GET/PUT/api/settingsRead/update app settings
GET/api/usersList users (admin)
POST/api/users/inviteInvite a user (admin)
GET/track/open/:tokenOpen tracking pixel
GET/track/click/:tokenClick redirect + tracking
GET/track/unsubscribe/:tokenUnsubscribe page
GET/api/campaigns/:id/settingsCampaign AI / mailbox settings
PUT/api/campaigns/:id/settingsUpsert campaign settings
POST/api/campaigns/:id/enrichStart LLM enrichment job
GET/api/campaigns/:id/enrichedList enriched rows
PUT/api/campaigns/:id/enriched/approve-allApprove all pending
PUT/api/campaigns/:id/enriched/reject-allReject all pending
PUT/api/campaigns/:id/enriched/:emailId/approveApprove one
PUT/api/campaigns/:id/enriched/:emailId/rejectReject one
POST/api/campaigns/:id/send-enrichedSend approved enriched
GET/api/campaigns/:id/inboxInbound messages

7. Tracking & compliance

Open tracking

A transparent 1×1 pixel image is injected into each campaign email. When the recipient’s email client loads the image, an “opened” event is recorded.

Click tracking

All links in campaign emails are rewritten to pass through the /track/click/:token endpoint. The system records the click event and redirects the recipient to the original URL.

Unsubscribe

Standard sends: include a link to /track/unsubscribe/:token. Enriched + reply mode: when enabled, sends omit List-Unsubscribe; recipients reply to opt out. Inbound messages are classified (keywords, then LLM), the contact is unsubscribed, and an auto-reply is sent.

Compliance notes

8. AI enrichment & review

OpenAI rewrites each recipient’s subject/body from the merged template. Operators approve or reject in /campaigns/:id/enrichment, then Send approved. Configure tone, instructions, send-from, and forward/BCC on the campaign detail card or at campaign creation.

Contacts may store enrichment_data (JSON) for extra context (role, company, Apollo exports).

9. Reply mailbox (Microsoft 365)

With MS365_IMAP_* set, the API polls the inbox, processes unseen mail, unsubscribes or forwards per campaign_settings, and logs rows in inbox_messages.

10. Campaign builder (2026 refresh)

Slimmer GrapesJS panels, refined toolbar, collapsible merge-field hints, and an Enrich & review action when AI is enabled.

11. Architecture & data flow diagrams

High-level relationships and the two new pipelines (enrichment and inbound reply handling). Diagrams render in modern browsers via Mermaid.

flowchart LR subgraph Client["Browser"] UI["React SPA"] end subgraph API["Application tier"] EX["Express API"] end subgraph Data["Data and auth"] PG[("PostgreSQL")] SB["Supabase Auth JWT"] end subgraph External["External services"] OAI["OpenAI API"] M365["Microsoft 365 SMTP/IMAP"] MH["MailHog dev SMTP"] end UI --> SB UI --> EX EX --> PG EX --> OAI EX --> M365 EX --> MH
Fig. 1 — System context: SPA, API, database, and outbound/inbound email paths.
flowchart TD A["Merged template per recipient"] --> B["POST /api/campaigns/:id/enrich"] B --> C["OpenAI rewrites subject/body"] C --> D["Rows in campaign_enriched_emails pending"] D --> E["Review UI /campaigns/:id/enrichment"] E --> F{"Approve?"} F -->|Yes| G["Status: approved"] F -->|No| H["Status: rejected"] G --> I["POST /api/campaigns/:id/send-enriched"] I --> J["SMTP send + tracking pixel/links"]
Fig. 2 — AI enrichment: async generation, human approval, then send approved rows only.
flowchart TD P["IMAP poll INBOX unseen"] --> Q{"Unsubscribe intent?"} Q -->|Keywords or LLM| R["Mark contact unsubscribed"] Q -->|Other reply| S["Forward to auto_forward_emails"] R --> T["Send auto-reply message"] P --> U["Log inbox_messages"]
Fig. 3 — Reply mailbox: classify inbound mail, unsubscribe or forward, audit trail.

Generated from Scope_and_User_Guide.md. Open this file from the docs/ folder so screenshot paths resolve correctly. Section 11 diagrams use Mermaid loaded from a CDN.