Skip to content

MERN Mail: a MERN email campaign manager for lead generation outreach

A case study in building outreach email on top of the sender's own SMTP accounts, with templates, per-send audit logs and two levels of access, in a TypeScript MERN stack.

MERN Mail dashboard with charts of email send statistics and a sidebar for templates, SMTP accounts, logs and profile

MERN Mail is a MERN email campaign manager I built in TypeScript for lead generation outreach. Users connect their own SMTP accounts with app passwords, write reusable templates and send through Nodemailer, while every email is written to an audit log. Admins oversee all users and the global log. It runs on React, Node.js and MongoDB with JWT and bcrypt authentication.

The problem

Cold outreach for lead generation is repetitive work done by hand in an ordinary inbox: the same introduction pasted again and again, sent from whichever account happens to be signed in, with no shared record of who was contacted and when. Once more than one person does it, nobody can say with confidence what has already gone out.

Bulk-mail services solve the volume but send from their own infrastructure, which is not what outreach from a person's real mailbox needs. The tool I wanted kept mail going out through the sender's own accounts, made templates reusable, and gave whoever ran the operation a single view of every message sent by every user.

My role

I built MERN Mail across the stack in TypeScript: the React interface with Tailwind CSS and shadcn/ui, the Node.js API, the MongoDB data model, authentication and password reset, the Nodemailer sending pipeline, and the statistics dashboards for users and admins.

Architecture and tech stack

Nodemailer on the user's own SMTP accounts

Sending through each user's SMTP account means the email arrives from a real mailbox the recipient can reply to, and the user's sent folder stays a true record. A user can connect several accounts and choose one per send. Accounts are added with app passwords, so the main account password never enters the app and an app password can be revoked from the provider without touching anything else.

Two roles with different scopes

The access model is deliberately small: a User manages their own SMTP accounts, templates and sent mail, and an Admin can see every user and the global email log. Two levels covered what the product needed, and every API route checks the role from the token rather than trusting what the interface chooses to show. For a three-tier version of the same idea, see the Team Management System, which separates Admin, Manager and Team Member.

An audit log written on every send

Each email the app sends is recorded with the sender and account it went through. Users read their own entries, and admins read all of them. The dashboard statistics are built from the same log, so the charts and the history can never disagree about how much mail went out.

TypeScript on both sides

Templates, accounts, log entries and users pass between React and the API many times in a single session. Writing both ends in TypeScript caught mismatched fields while I was building, before they could reach a send.

Key features

  • Multi-account SMTP: connect several sending accounts with app passwords and pick one per email.
  • Template management for reusable outreach messages.
  • Sending through Nodemailer from the selected account.
  • Audit logs for every email, per user and globally for admins.
  • Dashboards that chart send statistics.
  • Admin and User roles with JWT authentication and bcrypt-hashed passwords.
  • Profile management and a crypto-secured forgot-password flow.

Challenges and how I solved them

Holding credentials for someone else's mailbox

Problem
To send as a user, the server needs SMTP credentials for that user's account. Asking people for their real email password would be a serious risk for them and an obvious reason not to use the tool.
Solution
Accounts are connected with app passwords instead. An app password only works for mail access, is generated by the email provider, and can be revoked there at any moment, so a user stays in control of what the app can do with their mailbox.

A password reset that cannot be guessed

Problem
Forgot-password links are a common way into an account. A reset link built from anything predictable, such as a user ID or a timestamp, can be forged by someone who never had access to the inbox.
Solution
Reset tokens are generated with Node's crypto module, so the link carries a random value that cannot be derived from anything about the user. Passwords themselves are stored only as bcrypt hashes.

One log, two audiences

Problem
Users need to see what they sent, and admins need to see everything. Two separate logs would drift apart, and a single log returned in full to everyone would expose each user's outreach to the others.
Solution
There is one log collection, and the API scopes every query by role: a User's request is filtered to their own entries, an Admin's is not. The same scoping feeds the dashboard charts, so each person sees statistics only for the mail they are allowed to see.

Results

MERN Mail turns outreach into a repeatable process: write a template once, send it from the right account, and find every message afterwards in a log that an admin can review across the whole team. It shares its stack with my Transaction Tracker finance app, but the hard parts here were different: credentials for third-party mailboxes, delivery through external SMTP servers, and an audit trail that had to hold up for oversight.

What's next

Ideas for a next version include scheduled sends and per-recipient personalisation fields in templates, which would need to be confirmed before they are described as planned.

Need a full-stack developer for a product like this?

I work remotely with teams in the US, Canada, the UK, Europe and the Gulf, and with clients across Pakistan. Based in Pakistan (PKT, UTC+5), available for freelance and full-time roles.