How I Automated My Email Welcome Message Using AWS Lambda + SMTP
Sending a welcome email after signup is one of the easiest ways to improve user experience.
Instead of using a third-party automation tool, I built a simple serverless workflow using:
- AWS Cognito (authentication)
- AWS Lambda (backend logic)
- Nodemailer + SMTP (email delivery)
Here’s exactly how I set it up.
Overview
Flow:
- User signs up via Cognito
- Cognito triggers a Post Confirmation Lambda
- Lambda sends a welcome email via SMTP
1. Set Up Cognito Trigger
- Go to AWS Cognito
- Open your User Pool
- Navigate to:
User Pool → Authentication → Extensions → Add Lambda Trigger - Find Post confirmation trigger
- Attach your Lambda function
2️. Create the Lambda Function
- Runtime:
Node.js
Starter code:
export const handler = async (event) => {
const email = event.request.userAttributes.email;
const name = event.request.userAttributes.name || "there";
return event;
};
3️. Install Nodemailer
npm install nodemailer
4️. Configure SMTP Transport
Configure your mail transporter:
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT), // usually 587 or 465
secure: process.env.SMTP_PORT == "465", // true for 465, false for 587
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
Environment Variables
Set these in Lambda:
SMTP_HOST=smtp.yourprovider.com
SMTP_PORT=587
SMTP_USER=your_user
SMTP_PASS=your_password
5️. Create Email Templates
HTML
const getHtmlTemplate = (name) => `
<html>
<body>
<h1>Welcome, ${name} 🎉</h1>
<p>We're excited to have you on board.</p>
<p>Start exploring and let us know if you need anything.</p>
</body>
</html>
`;
Plain Text
const getTextTemplate = (name) => `
Welcome, ${name}!
We're excited to have you on board.
Let us know if you need anything.
`;
6️. Send the Email
Full Lambda:
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: process.env.SMTP_PORT == "465",
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
const getHtmlTemplate = (name) => `
<html>
<body>
<h1>Welcome, ${name} 🎉</h1>
<p>We're excited to have you on board.</p>
</body>
</html>
`;
const getTextTemplate = (name) => `
Welcome, ${name}!
We're excited to have you on board.
`;
export const handler = async (event) => {
const email = event.request.userAttributes.email;
const name = event.request.userAttributes.name || "there";
try {
await transporter.sendMail({
from: `"Your App" <${process.env.SMTP_USER}>`,
to: email,
subject: "Welcome to our platform 🎉",
text: getTextTemplate(name),
html: getHtmlTemplate(name),
});
console.log("Email sent to:", email);
} catch (err) {
console.error("Email error:", err);
}
return event;
};
7️. Attach the Trigger
Go back to:
Cognito → User Pool → Authentication → Extensions → Add Lambda Trigger
Attach Lambda to:
→ Post Confirmation
Test It
- Create a new user
- Confirm signup
- Check inbox
Tips
- Use a real SMTP provider:
- Amazon SES
- Mailgun
- SendGrid
- Port 465 = secure (SSL)
- Port 587 = STARTTLS
- Always use environment variables
Improvements
- Add branding to HTML emails
- Use a template engine (e.g. Handlebars)
- Add retry logic or SQS queue
- Track delivery / opens
- Import templates from files if content-heavy
This setup gives you:
- Full control over emails
- No external automation tools
- A clean serverless architecture
If you’re already using Cognito, this is a super easy upgrade.
Happy building 🚀