Skip to main content
Send transactional emails from AWS Lambda using Lettr’s HTTP API. Lambda’s event-driven architecture pairs perfectly with Lettr — no persistent connections, no background workers, just simple HTTP requests. Using Cursor? Jump straight in using this prompt

Prerequisites

Before you begin, make sure you have:

API Key

Create an API key in the Lettr dashboard

Verified Domain

Add and verify your sending domain
You’ll also need:
  • AWS account with Lambda access and appropriate IAM permissions
  • Node.js 18.x or 20.x runtime
  • AWS CLI installed and configured (optional, but recommended)

Quick Setup

1

Create a Lambda function

Create a new Lambda function in the AWS Console or via AWS CLI:
aws lambda create-function \
  --function-name send-email \
  --runtime nodejs20.x \
  --role arn:aws:iam::YOUR_ACCOUNT_ID:role/lambda-execution-role \
  --handler index.handler \
  --zip-file fileb://function.zip
2

Add environment variables

Set your Lettr API key as an environment variable:
aws lambda update-function-configuration \
  --function-name send-email \
  --environment "Variables={LETTR_API_KEY=lttr_your_api_key_here,FROM_EMAIL=noreply@yourdomain.com}"
For production, use AWS Secrets Manager instead of environment variables. See the Advanced Guide for details.
3

Deploy your function

Deploy the function code (see examples below) and test it:
aws lambda invoke \
  --function-name send-email \
  --payload '{"body":"{\"to\":\"user@example.com\",\"subject\":\"Test\",\"html\":\"<p>Hello!</p>\"}"}' \
  response.json

Node.js Implementation

Install the SDK in your Lambda function:
npm install lettr
Then create your handler:
import { Lettr } from 'lettr';

const lettr = new Lettr(process.env.LETTR_API_KEY);

export const handler = async (event) => {
  try {
    // Parse request body (API Gateway proxy integration)
    const body = JSON.parse(event.body || '{}');
    const { to, subject, html, text } = body;

    // Validate required fields
    if (!to || !subject || (!html && !text)) {
      return {
        statusCode: 400,
        body: JSON.stringify({
          error: 'Missing required fields: to, subject, and html or text',
        }),
      };
    }

    // Send email via Lettr
    const result = await lettr.emails.send({
      from: process.env.FROM_EMAIL || 'noreply@yourdomain.com',
      to: Array.isArray(to) ? to : [to],
      subject,
      html,
      text,
    });

    console.log(`Email sent successfully. Request ID: ${result.request_id}`);

    return {
      statusCode: 200,
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        success: true,
        requestId: result.request_id,
        accepted: result.accepted,
      }),
    };
  } catch (error) {
    console.error('Failed to send email:', error);

    return {
      statusCode: error.status || 500,
      body: JSON.stringify({
        error: error.message || 'Internal server error',
      }),
    };
  }
};
The Lettr SDK automatically handles retries for transient failures and provides better error messages than raw fetch calls.

What’s Next

Advanced Guide

Learn about deployment strategies, event triggers, and best practices

Vercel Functions

Deploy on Vercel

Cloudflare Workers

Deploy on Cloudflare

API Reference

Complete API documentation