KAMPAIGN
Local, secure, bulk email automation
VERSION 0.1.0 | NEXT.JS 16 + REACT 19 | MPL-2.0
github.com/kaushalrijal/kampaign
CONTENTS
GETTING STARTED
Overview
Features
Requirements
Configuration (.env)
Run the App

CORE WORKFLOW
Dashboard
Import Recipients
Design Template
Attachments
Configure & Send
Settings

REFERENCE
Template Placeholders
Data & Storage
Internal API
Troubleshooting
Project Structure


QUICK LINKS
GitHub Repo

STATUS: READY TO SEND
LOCAL-FIRST STORAGE
OVERVIEW
Kampaign runs locally and uses your SMTP credentials to send. Contacts, templates, and campaign history stay on your machine. There is no hosted backend in the loop. The source is available on GitHub if you want to review or extend it.

FEATURES
  • CSV and Excel import
  • Template editor with placeholders
  • Preview before sending
  • Campaign history
  • Broadcast and personalized attachments
  • SMTP connection test
  • Docker and Node.js support
  • Open source

REQUIREMENTS
  • Node.js 18+ for local dev
  • Docker and Docker Compose if you prefer containers
  • SMTP credentials from your email provider

CONFIGURATION (.env)

Create a .env file in the project root:

SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your_email_here
SMTP_APP_PASSWORD=your_app_password_here
APP_PORT=3000
                            

Keep this file local. It is excluded from Git and Docker builds.

SMTP transport uses secure=false by default. Use port 587 for TLS.

Getting credentials:

  • Gmail: create an App Password in your Google Account security settings. App Passwords guide
  • Other providers: use their SMTP username and password or token from their help docs.

RUN THE APP
Development (no Docker)
npm install
npm run dev
                                  

Open: http://localhost:3000

Production (no Docker)
npm run build
npm start
                                  
Docker (Production)
docker compose -f docker-compose.prod.yml build
docker compose -f docker-compose.prod.yml up
                                  

Open: http://localhost:3000

Docker (Development)
docker compose -f docker-compose.dev.yml up
                                  

Hot reload with containerized dependencies.


DASHBOARD
View past campaigns stored in IndexedDB. Each row shows name, subject, recipients, completion date, and status. Click VIEW to open details and logs.

IMPORT RECIPIENTS
Formats: CSV, XLS, XLSX. The first sheet is used.
How it works:
  • Each column becomes a template placeholder.
  • A preview shows the first 10 rows and detected headers.
Tip: Make sure one column contains the recipient email address.

DESIGN TEMPLATE
Subject: supports placeholders like {name}.
Body: rich text editor with headings, lists, alignment, links, and code blocks.

Placeholders:
  • Use {column_name} to insert a field.
  • Click a header chip to insert it, or type "{" to autocomplete.

ATTACHMENTS
Two modes:
  • Broadcast: sent to every recipient.
  • Personalized: matched per recipient using a filename rule.
How to use:
  1. Upload files.
  2. Enable custom rules if you want a mix of broadcast and personalized files.
  3. Mark each file as Broadcast or Personalized.
  4. Add one or more rules. A rule is a filename pattern with placeholders.
Rule examples:
{name}_invitation.pdf
{student_id}_report.pdf
                            
Matching behavior:
  • The rule is rendered for each recipient using their data.
  • If a file with that exact name exists in your uploads, it is attached.
  • If there is no match, that personalized file is skipped for that recipient.
Tips:
  • Use exact filenames, including extensions.
  • Keep placeholder names identical to the column headers.

CONFIGURE & SEND
Summary: counts recipients, attachments, and readiness.
Campaign name: used in history and log files.
Email header: choose the column that contains recipient email addresses.
SMTP test: verify credentials before sending.

Send flow:
  1. Click SEND NOW to validate required fields.
  2. Preview a rendered email for a random contact.
  3. Use RANDOMIZE to preview another contact.
  4. Confirm SEND to deliver.
Validation rules:
  • Required: subject, at least one contact, selected recipient header.
  • Warnings: missing campaign name, empty body, or no attachments.
Schedule: the button is UI only for now.

SETTINGS (SMTP CONFIGURATION)
This page collects SMTP host, port, username, and app password. Saving is UI only. SMTP tests use values from .env.

TEMPLATE PLACEHOLDERS
Syntax: use curly braces with column headers, for example {first_name}.
If a placeholder does not match a column or the value is empty, it stays unchanged.

DATA & STORAGE
Session data lives in a client store and resets after send or refresh. Campaign history is stored in IndexedDB. Delivery logs are saved to logs/campaigns. Attachments are written to tmp during send, then removed.

INTERNAL API
GET /api/smtp/test tests SMTP credentials from .env.

POST /api/campaign/send accepts multipart/form-data with a JSON payload and files, sends mail, and returns counts and log file path.

GET /api/campaign/logs/[campaignSlug] returns log entries for the campaign.

TROUBLESHOOTING
SMTP not configured
Check SMTP_HOST, SMTP_PORT, SMTP_USER, and SMTP_APP_PASSWORD in .env.

Gmail login fails
Use an App Password and enable 2FA.

Placeholders not replaced
Match column names exactly, including case.

Personalized attachments missing
Check that filenames match the rendered rule and include the extension.

PROJECT STRUCTURE
app/
  (dashboard)/          Dashboard + campaign detail pages
  (campaign)/           Import -> Design -> Attachments -> Configure flow
  api/                  SMTP test, send, logs
  settings/             SMTP settings UI (currently UI-only)
components/
  design/               Rich text editor
  shared/               Header, sidebar, drag/drop, footer
lib/
  api.ts                Client API helpers
  store/                Zustand state
  template/             Placeholder rendering
  server/               SMTP config + logging
  db/                   IndexedDB (Dexie) storage

Kampaign documentation - local-first bulk email automation BACK TO TOP