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: You’ll also need:
  • AWS account with Lambda access and appropriate IAM permissions
  • Node.js 18.x or 20.x runtime (or Python 3.9+)
  • 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.

Python Implementation

Install the SDK:
pip install lettr -t .
Then create your handler:
import json
import os
from lettr import Lettr

lettr = Lettr(os.environ['LETTR_API_KEY'])

def handler(event, context):
    try:
        # Parse request body
        body = json.loads(event.get('body', '{}'))
        to = body.get('to')
        subject = body.get('subject')
        html = body.get('html')

        # Validate required fields
        if not to or not subject or not html:
            return {
                'statusCode': 400,
                'body': json.dumps({
                    'error': 'Missing required fields: to, subject, html'
                })
            }

        # Send email
        result = lettr.emails.send(
            from_email=os.environ.get('FROM_EMAIL', 'noreply@yourdomain.com'),
            to=[to] if isinstance(to, str) else to,
            subject=subject,
            html=html
        )

        print(f"Email sent successfully. Request ID: {result['request_id']}")

        return {
            'statusCode': 200,
            'body': json.dumps({
                'success': True,
                'requestId': result['request_id'],
                'accepted': result['accepted']
            })
        }

    except Exception as e:
        print(f"Failed to send email: {str(e)}")

        return {
            'statusCode': 500,
            'body': json.dumps({'error': str(e)})
        }

What’s Next