This project implements a REST API on AWS Lambda that sends emails using Amazon SES (Simple Email Service). The API is built with the Serverless Framework and supports both Node.js and Python implementations.
Key Features:
- ✅ Sends emails via AWS SES
- ✅ HTTP API Gateway integration
- ✅ Comprehensive error handling
- ✅ Input validation (email format, required fields)
- ✅ Proper HTTP status codes (200, 400, 500)
- ✅ CORS enabled for cross-origin requests
- ✅ Environment-based configuration
- ✅ Production-ready code
Client (curl/Postman/Frontend)
↓
API Gateway (HTTP API)
↓
AWS Lambda Function
↓
Amazon SES
↓
Email Recipient
Before you start, ensure you have:
- AWS Account - with appropriate IAM permissions
- Node.js - v14 or higher (for Node.js version or package management)
- Python - v3.11 or higher (for Python version)
- AWS CLI - installed and configured with credentials
- Serverless Framework - installed globally
# Install Node.js (if not already installed)
# Visit: https://nodejs.org/
# Install Serverless Framework globally
npm install -g serverless
# Install AWS CLI
# Windows: https://awscli.amazonaws.com/AWSCLIV2.msi
# macOS: brew install awscli
# Linux: curl + unzip (see detailed guide)- Log into AWS Console at https://console.aws.amazon.com/
- Navigate to IAM → Users → Create user
- Enable Programmatic access
- Attach these policies:
AWSLambdaFullAccessAmazonAPIGatewayAdministratorAmazonSESFullAccessCloudFormationFullAccessIAMFullAccessAmazonS3FullAccess
- Save Access Key ID and Secret Access Key
aws configureEnter when prompted:
AWS Access Key ID: YOUR_ACCESS_KEY_ID
AWS Secret Access Key: YOUR_SECRET_ACCESS_KEY
Default region name: us-east-1
Default output format: json
Verify configuration:
aws sts get-caller-identityBefore sending emails, verify your sender email address in SES:
# Verify sender email
aws ses verify-email-identity --email-address your-email@example.com --region us-east-1
# Check verification status (should show VerificationStatus: Success)
aws ses get-identity-verification-attributes --identities your-email@example.com --region us-east-1Check your inbox and click the verification link from AWS.
email-api/
├── handler.js (or handler.py for Python)
├── serverless.yml
├── package.json (Node.js) or requirements.txt (Python)
├── .gitignore
└── README.md
mkdir email-api
cd email-apiCopy the following files from the provided templates:
service: email-api
frameworkVersion: '3'
provider:
name: aws
runtime: nodejs18.x # or python3.11 for Python version
region: us-east-1
environment:
SENDER_EMAIL: 'your-verified-email@example.com'
AWS_REGION: 'us-east-1'
iam:
role:
statements:
- Effect: Allow
Action:
- ses:SendEmail
- ses:SendRawEmail
Resource: '*'
functions:
sendEmail:
handler: handler.sendEmail
timeout: 30
events:
- httpApi:
path: /send-email
method: post
plugins:
- serverless-offlineSee the provided template file.
See the provided template file.
{
"name": "email-api",
"version": "1.0.0",
"description": "Serverless email API using AWS SES",
"main": "handler.js",
"scripts": {
"deploy": "serverless deploy",
"offline": "serverless offline start",
"logs": "serverless logs -f sendEmail -t"
},
"dependencies": {
"aws-sdk": "^2.x.x"
},
"devDependencies": {
"serverless": "^3.x.x",
"serverless-offline": "^13.x.x"
}
}boto3==1.28.x
Edit serverless.yml and replace:
SENDER_EMAIL: 'your-verified-email@example.com' # Your verified SES emailFor Node.js:
npm installFor Python:
pip install -r requirements.txtPOST https://{api-id}.execute-api.{region}.amazonaws.com/send-email
{
"receiver_email": "recipient@example.com",
"subject": "Email Subject",
"body_text": "Email body text content here."
}{
"success": true,
"message": "Email sent successfully",
"messageId": "0100018c...",
"data": {
"to": "recipient@example.com",
"subject": "Email Subject"
}
}{
"success": false,
"message": "Invalid email format",
"statusCode": 400
}{
"success": false,
"message": "Internal server error",
"statusCode": 500
}# Install dependency
npm install
# Start local server
serverless offline startThe API will be available at http://localhost:3000
curl -X POST http://localhost:3000/send-email \
-H "Content-Type: application/json" \
-d '{
"receiver_email": "test@example.com",
"subject": "Test Email",
"body_text": "Hello from local testing!"
}'Create test_local.py:
#!/usr/bin/env python3
import json
from handler import send_email
test_event = {
'body': json.dumps({
'receiver_email': 'recipient@example.com',
'subject': 'Test Email',
'body_text': 'Testing locally'
})
}
class Context:
function_name = 'test-function'
memory_limit_in_mb = 128
if __name__ == '__main__':
response = send_email(test_event, Context())
print(json.dumps(json.loads(response['body']), indent=2))Run with:
python test_local.pyserverless deployserverless deploy --stage prodserverless deploy --aws-profile my-profile-nameserverless infoThis will show your API endpoint URL.
serverless infoLook for the endpoint output. Example:
endpoint: POST - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/send-email
curl -X POST https://YOUR-API-URL/send-email \
-H "Content-Type: application/json" \
-d '{
"receiver_email": "recipient@example.com",
"subject": "Production Test",
"body_text": "Testing from production!"
}'- Open Postman
- Create new POST request
- URL:
https://YOUR-API-URL/send-email - Headers:
Content-Type: application/json
- Body (raw JSON):
{ "receiver_email": "recipient@example.com", "subject": "Test", "body_text": "Hello" } - Click Send
Solution:
aws configureEnter your Access Key ID and Secret Access Key.
Verify:
aws sts get-caller-identitySolution: You're in SES sandbox mode. Verify both sender and receiver emails:
aws ses verify-email-identity --email-address sender@example.com --region us-east-1
aws ses verify-email-identity --email-address receiver@example.com --region us-east-1Solution:
Ensure your curl command includes the JSON body with -d flag:
curl -X POST http://localhost:3000/send-email \
-H "Content-Type: application/json" \
-d '{"receiver_email":"test@example.com","subject":"Test","body_text":"Hello"}'Solution:
- Ensure AWS credentials are configured:
aws configure - Increase timeout in
serverless.yml:timeout: 30
- Verify SES region matches AWS CLI region
Solution: Your IAM user lacks permissions. Add these policies:
AWSLambdaFullAccessAmazonAPIGatewayAdministratorAmazonSESFullAccessCloudFormationFullAccessIAMFullAccess
Problem: Can only send emails to verified addresses
Solutions:
- Option A: Verify all recipient emails in SES (limited testing)
- Option B: Request production access in SES Console
- Go to Sending Statistics
- Click Request a Sending Limit Increase
- Fill out the form
- AWS typically approves within 24-48 hours
- ✅ Free testing
- ❌ Can only send to verified emails
- ❌ Limited sending rate (1 email/second, 200/day)
- ❌ Cannot send to arbitrary recipients
- ✅ Send to any email address
- ✅ Higher sending limits
- ✅ Better delivery rates
- ❌ Requires production access request
- ❌ May incur costs if exceeding free tier
serverless logs -f sendEmail -t- Go to AWS Console
- Navigate to CloudWatch → Log Groups
- Find
/aws/lambda/email-api-dev-sendEmail - Check recent log events
aws ses get-send-statistics --region us-east-1| Service | Free Tier | Overage Cost |
|---|---|---|
| Lambda | 1M requests/month + 400K GB-seconds | $0.20 per 1M requests |
| API Gateway | 1M requests/month | $3.50 per 1M requests |
| SES | 62K emails/month | $0.10 per 1K emails |
Typical monthly cost for low-volume: $0 (within free tier)
serverless removeThis will delete:
- Lambda function
- API Gateway endpoint
- CloudFormation stack
- CloudWatch log groups
- Never commit AWS credentials -
.gitignoreis set up for this - Use IAM roles - In production, use Lambda execution roles instead of access keys
- Rotate access keys - Regularly rotate IAM user credentials
- Enable MFA - On your IAM user account
- Use environment variables - For sensitive data like
SENDER_EMAIL - Validate all inputs - Code already does this
- Use HTTPS only - API Gateway automatically uses HTTPS
- Monitor costs - Set up AWS billing alerts
You can set environment variables per stage:
provider:
environment:
SENDER_EMAIL: 'default@example.com'
# Different per stage
stages:
dev:
environment:
SENDER_EMAIL: 'dev@example.com'
prod:
environment:
SENDER_EMAIL: 'prod@example.com'Deploy to specific stage:
serverless deploy --stage prodconst sendEmail = async (email, subject, message) => {
const response = await fetch('https://YOUR-API-URL/send-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
receiver_email: email,
subject: subject,
body_text: message
})
});
return response.json();
};import requests
def send_email(email, subject, message):
response = requests.post(
'https://YOUR-API-URL/send-email',
json={
'receiver_email': email,
'subject': subject,
'body_text': message
}
)
return response.json()For issues or questions:
- Check Troubleshooting section above
- Review AWS SES documentation: https://docs.aws.amazon.com/ses/
- Check Serverless Framework docs: https://www.serverless.com/framework/docs
- Enable debug mode:
serverless deploy --debug
This project is provided as-is for educational and development purposes.
- ✅ Set up AWS account and IAM user
- ✅ Configure AWS CLI
- ✅ Create project files
- ✅ Deploy to AWS
- ✅ Test with curl/Postman
- ✅ Integrate with your application
- ✅ Monitor and maintain
Happy coding! 🚀