How to Send Email from AWS Lambda
Send transactional email from AWS Lambda using TypeScript and Python. Complete guide with handler examples, environment variables, API Gateway setup, and local testing.
To send email from AWS Lambda, create a function handler, install the SendPigeon SDK, and call pigeon.send(). No Amazon SES setup needed — SendPigeon is an HTTP API that works from any Lambda runtime.
Quick setup:
- Create a Lambda function with
nodejs22.xruntime npm install sendpigeonand zip with your handler- Set
SENDPIGEON_API_KEYenvironment variable - Call
pigeon.send()in your handler
Best for: Webhook handlers, event-driven emails, scheduled notifications, S3 upload confirmations.
Node.js Lambda Handler
Create the handler
// index.ts
import type { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda";
import { SendPigeon } from "sendpigeon";
const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);
export async function handler(event: APIGatewayProxyEventV2): Promise<APIGatewayProxyResultV2> {
const { to, name } = JSON.parse(event.body ?? "{}");
const { data, error } = await pigeon.send({
from: "hello@yourdomain.com",
to,
subject: `Hey ${name}!`,
html: `<h1>Welcome</h1><p>Thanks for signing up.</p>`,
});
if (error) {
return {
statusCode: error.status ?? 500,
body: JSON.stringify({ error: error.message }),
};
}
return {
statusCode: 200,
body: JSON.stringify({ emailId: data.id }),
};
}
Lambda doesn't run TypeScript natively. Use esbuild to bundle your handler into a single .mjs file before deploying. See the packaging step below.
Package and deploy
# Install dependencies
npm init -y && npm install sendpigeon && npm install -D esbuild @types/aws-lambda
# Bundle TypeScript to a single file
npx esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.mjs
# Zip the bundle (no node_modules needed — esbuild bundles everything)
cd dist && zip -r ../function.zip index.mjs && cd ..
# Create the function
aws lambda create-function \
--function-name send-email \
--runtime nodejs22.x \
--handler index.handler \
--role arn:aws:iam::123456789012:role/your-lambda-role \
--zip-file fileb://function.zip
# Set environment variables
aws lambda update-function-configuration \
--function-name send-email \
--environment "Variables={SENDPIGEON_API_KEY=sp_live_your_key_here}"
To update the function after changes:
npx esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.mjs
cd dist && zip -r ../function.zip index.mjs && cd ..
aws lambda update-function-code \
--function-name send-email \
--zip-file fileb://function.zip
Add an API Gateway Trigger
To call your Lambda from HTTP requests, add an API Gateway trigger:
- Open your Lambda function in the AWS Console
- Click Add trigger > select API Gateway
- Choose Create a new API > HTTP API
- Set security to Open (or add auth later)
- Click Add
You get a public URL like https://abc123.execute-api.us-east-1.amazonaws.com/send-email.
The handler receives API Gateway events with this shape:
// index.ts
import type { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda";
import { SendPigeon } from "sendpigeon";
const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);
export async function handler(event: APIGatewayProxyEventV2): Promise<APIGatewayProxyResultV2> {
const { to, name, message } = JSON.parse(event.body ?? "{}");
const { data, error } = await pigeon.send({
from: "contact@yourdomain.com",
to: "you@yourdomain.com",
subject: `Contact form: ${name}`,
html: `
<p><strong>From:</strong> ${name} (${to})</p>
<p><strong>Message:</strong></p>
<p>${message}</p>
`,
replyTo: to,
});
if (error) {
return {
statusCode: error.status ?? 500,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ error: error.message }),
};
}
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ emailId: data.id }),
};
}
Event-Driven Examples
Send Email on S3 Upload
Trigger a notification when a file is uploaded to S3:
// index.ts
import type { S3Event } from "aws-lambda";
import { SendPigeon } from "sendpigeon";
const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);
export async function handler(event: S3Event) {
const record = event.Records[0];
const bucket = record.s3.bucket.name;
const key = decodeURIComponent(record.s3.object.key);
await pigeon.send({
from: "notifications@yourdomain.com",
to: "team@yourdomain.com",
subject: `New file uploaded: ${key}`,
html: `
<h1>New Upload</h1>
<p>A new file was uploaded to S3:</p>
<p><strong>Bucket:</strong> ${bucket}</p>
<p><strong>Key:</strong> ${key}</p>
<p><strong>Size:</strong> ${record.s3.object.size} bytes</p>
`,
});
return { statusCode: 200 };
}
Scheduled Email with EventBridge
Use an EventBridge (CloudWatch Events) schedule to send a daily report:
// index.ts
import { SendPigeon } from "sendpigeon";
const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);
export async function handler() {
const stats = await getStats();
await pigeon.send({
from: "reports@yourdomain.com",
to: "team@yourdomain.com",
subject: `Daily report — ${new Date().toLocaleDateString()}`,
html: `
<h1>Daily Report</h1>
<p>New users: ${stats.newUsers}</p>
<p>Revenue: $${stats.revenue}</p>
<p>Active sessions: ${stats.sessions}</p>
`,
});
return { statusCode: 200 };
}
Python Lambda Handler
Use the SendPigeon Python SDK for Python Lambda functions:
# Install SDK into a package directory
pip install sendpigeon -t package/
cd package && zip -r ../function.zip . && cd ..
zip function.zip lambda_function.py
# lambda_function.py
import json
import os
from sendpigeon import SendPigeon
client = SendPigeon(os.environ["SENDPIGEON_API_KEY"])
def lambda_handler(event, context):
body = json.loads(event["body"])
result = client.send(
from_="hello@yourdomain.com",
to=body["to"],
subject=f"Hey {body['name']}!",
html="<h1>Welcome</h1><p>Thanks for signing up.</p>",
)
if result.error:
return {
"statusCode": result.error.status or 500,
"body": json.dumps({"error": result.error.message}),
}
return {
"statusCode": 200,
"body": json.dumps({"emailId": result.data.id}),
}
The Python SDK uses from_ (with trailing underscore) instead of from because from is a reserved keyword in Python.
Environment Variables
Set environment variables in the AWS Console (Configuration > Environment variables) or via CLI:
aws lambda update-function-configuration \
--function-name send-email \
--environment "Variables={SENDPIGEON_API_KEY=sp_live_xxx,FROM_EMAIL=hello@yourdomain.com}"
The --environment flag replaces all environment variables. Include existing variables when adding new ones, or use the AWS Console for individual updates.
Access them in your handler:
// Node.js
const apiKey = process.env.SENDPIGEON_API_KEY;
# Python
api_key = os.environ["SENDPIGEON_API_KEY"]
Handle Errors
The SDK returns { data, error } in Node.js and result.data / result.error in Python — no exceptions to catch:
const { data, error } = await pigeon.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Hello",
html: "<p>Hello!</p>",
});
if (error) {
console.error(`Email failed: ${error.message}`);
return {
statusCode: error.status ?? 500,
body: JSON.stringify({ error: error.message }),
};
}
console.log(`Email sent: ${data.id}`);
Test Locally
Test your email-sending logic locally before deploying:
npx @sendpigeon-sdk/cli dev
Captured emails are viewable at localhost:4100. See the local email testing guide for full setup.
Frequently Asked Questions
Do I need Amazon SES to send email from Lambda?
No. SendPigeon is a standalone email API — your Lambda makes a single HTTPS request. No SES configuration, no sandbox mode to deal with, no verified identity setup in AWS. Just set your API key and send.
How do I include npm packages in a Lambda function?
Use esbuild to bundle your TypeScript handler into a single file — no node_modules needed in the zip. For packages used across multiple functions, create a Lambda Layer with the dependencies in a nodejs/node_modules directory.
Which Node.js runtime should I use?
Use nodejs22.x for new functions. It has stable global fetch, native ESM support, and long-term support through April 2027. nodejs24.x is also available.
Can I send email from a Python Lambda?
Yes. Install the SendPigeon Python SDK (pip install sendpigeon), package it with your function code, and call client.send() from your handler. See the Python example above.
How do I trigger the Lambda from an HTTP request?
Add an API Gateway trigger — choose HTTP API (not REST API) for the simplest setup. This gives you a public HTTPS URL that forwards requests to your Lambda function.
Next Steps
- Set up DKIM, SPF, and DMARC on your sending domain
- Review the email deliverability checklist
- Build templates with our free visual email builder
- Browse our email templates for ready-to-use HTML
Other Platforms
Deploying elsewhere? We have guides for:
See all framework guides.