A multi-tenant, AI-assisted email (and SMS) campaign management platform. Organizations manage contacts, build reusable templates, run bulk campaigns across multiple providers (AWS SES, Gmail/Outlook SMTP), and get real-time delivery notifications β with generative AI template creation and an agentic, natural-language contact-management assistant built in.
Live Demo: https://emailcampaign.musfiqdehan.com
- Project Overview
- Features
- Tech Stack
- Architecture
- Project Structure
- Getting Started
- Running Locally (Manual Setup)
- Running with Docker
- API Documentation
- Testing & Linting
- AI Tools Used
- Key Learnings
- Areas for Improvement
- License
This is an end-to-end Email Campaign Management Platform that allows organizations to manage contacts, create reusable templates, launch bulk email/SMS campaigns, and monitor delivery progress from a single system. It's built as a production-style multi-tenant application β each organization owns its own contacts, templates, providers, and campaigns, with role-based membership (owner/admin/member).
Beyond core campaign management, the platform includes:
- Generative AI support for email template creation (Gemini, with a DeepSeek fallback)
- An agentic, natural-language contact-management endpoint β manage contacts by describing what you want in plain English
- A bidirectional mailbox with first-party email open/click tracking
- Multi-provider sending (AWS SES, Gmail/Outlook SMTP) resolved per-organization at send time
- Real-time in-app and web-push notifications over WebSockets
- Create, schedule, launch, pause, resume, cancel, and duplicate email campaigns
- Test-send and live preview before launch
- Per-campaign analytics with delivery/open/click stats and stat refresh
- Contacts and contact lists with bulk CSV import
- Agentic, natural-language contact operations (create/update/search contacts via AI)
- Contact segmentation and list-level stats
- Rich HTML template editor with dynamic personalization variables
- AI-generated template content (Gemini primary, DeepSeek fallback)
- Template categories, admin-curated templates, and previews
- AWS SES and Gmail/Outlook SMTP, resolved per-organization via a config hierarchy
- Encrypted storage of provider credentials
- Provider health checks and rate/quota limiting (per-second/minute/hour/day)
- Delivery event webhooks from SES, SendGrid, and Brevo, plus a delivery log with resend/forward
- Bidirectional mailbox sync (send & receive) with a message inbox
- First-party open/click tracking pixels & redirect links
- Rule-based automation for triggered emails
- SMS and WhatsApp trigger campaigns (via Twilio)
- WebSocket-based in-app notifications (Django Channels)
- Web push notifications (VAPID)
- Organizations, membership roles (owner/admin/member), and per-org settings
- Platform-admin views for cross-tenant provider/organization management
- Responsive dashboard with dark/light theme (system-aware)
- Animated, interactive landing page (SVG micro-interactions, pricing, growth/process visuals)
- JWT authentication (access + refresh) with real DB-backed users
- Encrypted provider credentials, soft-delete on core models, org-scoped permissions
| Technology | Purpose |
|---|---|
| Python 3.12 | Core language |
| Django 5.2 + DRF 3.15 | Web framework & REST API |
| PostgreSQL | Primary database |
| Redis | Celery broker/result backend & Channels layer |
| Celery 5.3 + Celery Beat | Async & scheduled tasks (bulk email/SMS, automation) |
| Django Channels + Daphne | WebSocket notifications (ASGI) |
| SimpleJWT | JWT authentication |
| drf-spectacular | OpenAPI schema, Swagger UI, ReDoc |
| django-ses, boto3 | AWS SES email provider |
| Twilio | SMS / WhatsApp sending |
| SendGrid, Brevo webhooks | Alternate delivery provider + inbound/event webhooks |
| pywebpush / py-vapid | Web push notifications |
| google-genai (Gemini) | AI template generation & the agentic contact assistant |
| django-health-check | /api/v1/healthz/ liveness endpoint |
| Gunicorn / Daphne | Production servers (WSGI/ASGI) |
| pytest, flake8, black | Testing & code quality |
| Technology | Purpose |
|---|---|
| Next.js 16 (App Router) | React framework |
| React 19 + TypeScript 5 | UI & type safety |
| Tailwind CSS 4 | Styling |
Radix UI + class-variance-authority |
Accessible UI primitives (shadcn/ui-style) |
| React Hook Form + Zod | Forms & validation |
| next-themes | Dark/light theming |
| Axios | HTTP client |
| React Quill (new) | Rich text template editor |
| Sonner | Toast notifications |
| Lucide React | Icons |
| Technology | Purpose |
|---|---|
| Docker / Docker Compose | Containerization (per-service & unified) |
| Traefik | Reverse proxy & TLS termination in production |
| GitHub Actions | CI (check, migration check, manage.py test --parallel against Postgres+Redis service containers) |
| Nginx | Static/reverse-proxy config for select deployments |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT LAYER β
β Next.js 16 (React 19 + TypeScript) β
β App Router Β· Tailwind + Radix UI Β· Axios Β· WebSocket hooks β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β REST (JSON) + WebSocket
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β API LAYER β
β Django REST Framework Β· JWT Auth Β· Org-scoped permissions β
β Django Channels (ws/notifications/) served over Daphne (ASGI) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β APPLICATION LAYER (apps.campaigns) β
β Campaigns Β· Contacts Β· Templates Β· Providers Β· Automation β
β Mailbox & Tracking Β· SMS/WhatsApp Β· Notifications Β· Admin β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β PostgreSQL β β Redis β β Celery β
β (database) β β (broker/cache/ β β (async & beat β
β β β channel layer) β β scheduled tasks)β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββ
β AWS SES Β· Gmail/Outlook SMTP β
β Twilio (SMS/WhatsApp) β
β SendGrid / Brevo webhooks β
βββββββββββββββββββββββββββββββββ
Email sending flow: organization config β hierarchy_resolver resolves the effective provider config β ProviderBackendResolver builds the right Django email backend β unified_email_sender sends it β one code path supporting SES, SMTP, or console/test sending interchangeably.
Email-Campaign-Management-Platform/
βββ README.md # This file
βββ DEPLOYMENT.md # Full production/VPS deployment guide (Traefik + Cloudflare)
βββ LICENSE # Apache 2.0
βββ docker-compose.yml # Unified production stack (Postgres, Redis, backend, Celery, frontend)
βββ .env.example # Environment variable reference for the unified compose
βββ backend/ # Django REST API
βββ frontend/ # Next.js dashboard & landing site
βββ frontend-newsletter/ # Static embeddable newsletter signup widget
βββ mobile/ # Expo scaffold (minimal/unused)
βββ nginx/ # Reverse proxy config for select deployments
backend/
βββ manage.py
βββ requirements.txt
βββ Dockerfile / docker-compose.yml / docker_entrypoint.sh
βββ config/ # Django project config
β βββ settings.py
β βββ urls.py # /api/v1/auth, /api/v1/campaigns, /api/v1/healthz, /api/v1/schemas
β βββ asgi.py # Channels ProtocolTypeRouter (WebSocket)
β βββ wsgi.py
β βββ celery.py
βββ core/ # Response envelope & centralized exception handling
β βββ mixins.py # ResponseMixin (aliased as CustomResponseMixin)
β βββ exceptions.py # custom_exception_handler (DRF EXCEPTION_HANDLER)
β βββ utils.py
βββ apps/
β βββ authentication/ # User, Organization, OrganizationMembership, JWT, permissions
β β βββ models.py / views.py / serializers.py / permissions.py / signals.py
β β βββ services/
β β βββ management/commands/ # create_superuser, create_platform_admin,
β β create_user_organizations, assign_user_organization, list_users_orgs
β βββ campaigns/ # Core app β the bulk of the business logic
β β βββ models/ # campaign, contact, email_config, provider, automation_rule,
β β β email_tracking, notification, push, sms_config, mailbox, org_email_config
β β βββ views/ # campaign, admin, enhanced, template_operations, admin_templates,
β β β organization_admin, notification, push, sms_automation,
β β β email_automation, variable, ai_gen, contact_agent, mailbox,
β β β tracking, provider_webhooks, unsubscribe, debug
β β βββ serializers/ # campaign, admin, enhanced, push, mailbox, base
β β βββ utils/ # email_providers, unified_email_sender, hierarchy_resolver,
β β β tenant_service, crypto, template_utils, variable_registry,
β β β ai_client, sms_utils, push_utils, mailbox_sync, email_tracking
β β βββ management/commands/ # generate_encryption_key, sync_email_providers,
β β β check_email_providers_health
β β βββ tasks.py # Celery tasks for async/bulk sending
β β βββ backends.py # ProviderBackendResolver (SES/SMTP/console)
β β βββ consumers.py / routing.py # WebSocket notification consumer
β β βββ ses_event_handlers.py
β β βββ migrations/
β βββ notifications/, platform_admins/, template_managers/ # placeholder apps (not wired up)
β βββ utils/ # BaseModel (soft-delete), pagination, filters, throttles, mixins
βββ static/ Β· staticfiles/ # Collected static assets
βββ media/ Β· media_files/ # User-uploaded assets (logos, profile images)
apps.notifications,apps.platform_admins, andapps.template_managersare empty placeholder directories β notification and admin functionality actually lives insideapps.campaigns.
frontend/
βββ package.json / tsconfig.json / next.config.ts / eslint.config.mjs
βββ Dockerfile / docker-compose.yml
βββ app/ # Next.js App Router
β βββ page.tsx # Marketing landing page
β βββ layout.tsx / globals.css
β βββ login/ Β· signup/ Β· reset-password/ Β· verify-email/ Β· unsubscribe/
β βββ dashboard/ # Authenticated app
β βββ layout.tsx / page.tsx
β βββ campaigns/ Β· contacts/ Β· templates/ Β· inbox/
β βββ logs/ Β· notifications/ Β· team/ Β· admin/
β βββ settings/ Β· profile/
βββ components/
β βββ ui/ # Radix-based primitives (button, dialog, table, tabs, ...)
β βββ dashboard/ # sidebar, header, notification settings, floating AI agent input
β βββ landing/ # Animated SVG landing sections (hero, growth chart, network bg, ...)
β βββ brand-logo.tsx / editor.tsx / providers.tsx
βββ config/ # axios client, constants, template & general utils
βββ contexts/ # AuthContext
βββ hooks/ # useNotifications, usePushNotifications, useRealtimeUpdates, ...
βββ services/ # auth.ts, campaigns.ts, notifications.ts β thin API wrappers
βββ public/ # Static assets (logo, icons, service worker)
- Python 3.12+
- Node.js 20+ and npm
- PostgreSQL (14+) and Redis (7+) β locally installed, or run via Docker (see below)
- Docker + Docker Compose (optional, for the containerized workflow)
The backend and frontend each read from their own .env files, plus the repo root has a .env.example used by the unified Docker Compose stack. Copy the example and fill in values before running anything:
cp .env.example .env # for the unified/root docker-compose.yml
cp backend/.env.example backend/.envKey variable groups (see .env.example for the full list with comments):
| Group | Variables |
|---|---|
| Django core | DEBUG, SECRET_KEY, SIGNING_KEY, EMAIL_CONFIG_ENCRYPTION_KEY, ALLOWED_HOSTS |
| PostgreSQL | POSTGRES_ENGINE, POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_PORT |
| Redis | REDIS_HOST, REDIS_PASSWORD |
| Frontend build args | NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WS_URL, NEXT_PUBLIC_VAPID_PUBLIC_KEY |
| Email / SMTP / SES | EMAIL_BACKEND, EMAIL_HOST(_USER/_PASSWORD), EMAIL_PORT, EMAIL_USE_TLS/SSL, DEFAULT_FROM_EMAIL |
| Web push (VAPID) | VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_CLAIM_EMAIL |
| AI providers | GEMINI_API_KEY, DEEPSEEK_API_KEY (fallback) |
| Sending limits | ORG_PROVIDER_MAX_RATE_PER_SECOND/MINUTE/HOUR, ORG_PROVIDER_MAX_DAILY_QUOTA |
| Bootstrap superuser | DJANGO_SUPERUSER_USERNAME/EMAIL/PASSWORD/FIRST_NAME/LAST_NAME |
Generate the required secrets with:
# EMAIL_CONFIG_ENCRYPTION_KEY
python manage.py generate_encryption_key
# SECRET_KEY / SIGNING_KEY
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
# VAPID keys (web push)
python -c "from py_vapid import Vapid; v = Vapid(); v.generate_keys(); print(v.private_pem().decode()); print(v.public_key)"cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # then fill in DB/Redis/secret values
# Make sure PostgreSQL & Redis are running and reachable per your .env
python manage.py migrate
python manage.py create_superuser --username admin --email admin@example.com --password <password> --force
python manage.py create_user_organizations # backfill an org for the new user, if needed
python manage.py runserver 8001 # plain dev server β fine for non-WebSocket work
# or, to exercise WebSocket notifications:
daphne -p 8001 -b 0.0.0.0 config.asgi:applicationThe checked-in
frontend/.env.localalready points tohttp://localhost:8001β running the backend on port8001(as above) means the frontend works with zero extra config. Use plainrunserveron its default port 8000 instead and the frontend just needsNEXT_PUBLIC_API_URL/NEXT_PUBLIC_WS_URLupdated to match.
The API is now available at http://localhost:8001/api/v1/.
cd frontend
npm install # use `npm ci --legacy-peer-deps` if npm complains (React 19 peer deps)
# frontend/.env.local already sets NEXT_PUBLIC_API_URL / NEXT_PUBLIC_WS_URL to http://localhost:8001 β edit if needed
npm run devThe dashboard is now available at http://localhost:3000.
Required for bulk email sending and scheduled automation β run these alongside the backend:
cd backend
celery -A config.celery worker --loglevel=info
celery -A config.celery beat --loglevel=info --scheduler django_celery_beat.schedulers:DatabaseSchedulerEach app ships its own docker-compose.yml for spinning it up in isolation. Both reference an external Docker network, so create it once before the first run:
# Backend β Django, PostgreSQL, Redis, Celery worker & beat
cd backend
cp .env.example .env
docker network create dokploy-network # skip if it already exists
docker compose up -d --build
docker compose exec backend python manage.py migrate
docker compose exec backend python manage.py create_superuser --username admin --email admin@example.com --password <password> --force
docker compose logs -f backend- API β
http://localhost:8001/api/v1/ - PostgreSQL β
localhost:5441, Redis βlocalhost:6391
# Frontend β Next.js (standalone build)
cd frontend
docker network create ecmp_network # skip if it already exists
docker compose up -d --build- App β
http://localhost:3001(defaults to talking to the backend above viaNEXT_PUBLIC_API_URL=http://localhost:8001/api/v1)
Stop either stack with docker compose down from its directory.
The root docker-compose.yml builds the full stack (PostgreSQL, Redis, backend on Daphne, Celery worker + beat, and the frontend) as it's actually deployed in production β fronted by Traefik on an external traefik_proxy network, with the app services only exposed (not published) to the host. It expects Cloudflare Origin TLS certs at nginx/certs/ and a real reverse proxy in front of it, so it isn't meant to be run standalone on a laptop.
For the full VPS deployment walkthrough (DNS, certs, firewall, docker compose up -d --build, health checks, and troubleshooting), see DEPLOYMENT.md.
Once the backend is running (adjust the port to match how you started it β 8001 in the examples above):
- Swagger UI β
http://localhost:8001/api/v1/schemas/swagger-ui/ - ReDoc β
http://localhost:8001/api/v1/schemas/redoc - OpenAPI schema (JSON) β
http://localhost:8001/api/v1/schemas/swagger.json - Health check β
http://localhost:8001/api/v1/healthz/
All application endpoints are namespaced under /api/v1/auth/ (authentication) and /api/v1/campaigns/ (campaigns, contacts, templates, providers, automation, SMS, mailbox, tracking, webhooks, admin).
# Backend (Django's own test runner, not pytest, despite pytest-django being installed)
cd backend
python manage.py test
python manage.py test apps.campaigns.tests.test_email_logs
python manage.py check
python manage.py migrate --check --dry-run
flake8
black --check .
# Frontend
cd frontend
npm run lint
npx tsc --noEmitCI (.github/workflows/deploy.yml) runs the same check β migration check β manage.py test --verbosity=2 --parallel sequence against Postgres + Redis service containers β mirror that locally before opening a PR.
- Claude Code CLI β code generation, problem-solving, and architectural guidance
- GitHub Copilot β real-time code suggestions and productivity improvements
During development, this project was an exercise in both system design and real-world production challenges:
AI Integration
- Integrated GenAI features across frontend and backend within an existing codebase
- Designed workflows for AI-assisted template generation and natural-language-driven operations
Email Campaign System Design
- Studied industry-standard email marketing platforms to understand campaign workflows and best practices
- Implemented dynamic email templates with variables and trigger-based campaign execution
- Built subscriber collection mechanisms using external APIs
Email Infrastructure & Deliverability
- Integrated AWS SES for scalable email automation
- Supported multiple email providers (SES, Gmail SMTP) with dynamic, per-organization provider selection
- Applied DNS-level configuration (SPF, DKIM, DMARC) to improve deliverability and avoid spam classification
Backend Engineering & Scalability
- Designed asynchronous email processing with Celery for bulk delivery
- Implemented real-time notifications using WebSockets for in-app and push notifications
DevOps & Deployment
- Deployed frontend and backend behind Traefik/Nginx on a cloud VPS
- Managed static assets and backups
- Built CI/CD pipelines with GitHub Actions for automated testing and deployment
Security & Best Practices
- Implemented unsubscribe mechanisms and email compliance standards
- Applied best practices for secure email template rendering, credential encryption, and campaign execution
- Expanding support for additional email providers beyond AWS SES and Gmail/Outlook SMTP
- Extending agentic (AI-driven) capabilities across more workflows within the platform
- Enhancing analytics with additional metrics β bounce rate, deliverability scoring, cohort engagement, etc.
- Wiring up the currently-placeholder
apps.notifications,apps.platform_admins, andapps.template_managersapps
Licensed under the Apache License 2.0 β see LICENSE for details.
