Skip to main content
Send transactional emails from Vercel serverless functions using Lettr’s HTTP API. Vercel’s global edge network and fast cold starts make it perfect for email-triggered API routes and server actions. Using Cursor? Jump straight in using this prompt

Prerequisites

Before you begin, make sure you have: You’ll also need:
  • Vercel account with a project deployed or ready to deploy
  • Next.js project (recommended) or a Node.js serverless function
  • Node.js 18.x or 20.x runtime

Quick Setup

1

Install the Lettr SDK

npm install lettr
The SDK provides a clean interface for sending emails with full TypeScript support.
2

Add environment variables

Add your API key via the Vercel dashboard or CLI:
vercel env add LETTR_API_KEY
When prompted, paste your API key (starts with lttr_). Then add your from email:
vercel env add FROM_EMAIL
For local development, create a .env.local file with the same variables. Vercel automatically loads .env.local in development mode.
3

Create an API route

Create a file at app/api/send/route.ts (App Router) or pages/api/send.ts (Pages Router) with the code examples below.
4

Deploy

vercel deploy
Your email API route will be available at https://your-domain.vercel.app/api/send.
For Next.js 13+ projects using the App Router, create a file at app/api/send/route.ts:
import { NextResponse } from 'next/server';
import { Lettr } from 'lettr';

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

export async function POST(request: Request) {
  try {
    const body = await request.json();
    const { to, subject, html, text } = body;

    // Validate required fields
    if (!to || !subject || (!html && !text)) {
      return NextResponse.json(
        { error: 'Missing required fields: to, subject, and html or text' },
        { status: 400 }
      );
    }

    // 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 NextResponse.json({
      success: true,
      requestId: result.request_id,
      accepted: result.accepted,
    });
  } catch (error: any) {
    console.error('Failed to send email:', error);

    return NextResponse.json(
      { error: error.message || 'Failed to send email' },
      { status: error.status || 500 }
    );
  }
}

// Configure function execution
export const maxDuration = 30; // Max duration in seconds (Pro plan and higher)
The maxDuration export configures the maximum execution time for the function. Email sending typically completes in under 1 second, but setting a higher limit accounts for retries and network latency.

Next.js Pages Router

For Next.js projects using the Pages Router, create a file at pages/api/send.ts:
import type { NextApiRequest, NextApiResponse } from 'next';
import { Lettr } from 'lettr';

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

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // Only allow POST requests
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  try {
    const { to, subject, html, text } = req.body;

    // Validate required fields
    if (!to || !subject || (!html && !text)) {
      return res.status(400).json({
        error: 'Missing required fields: to, subject, and html or text',
      });
    }

    // Send email
    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. Request ID: ${result.request_id}`);

    res.status(200).json({
      success: true,
      requestId: result.request_id,
      accepted: result.accepted,
    });
  } catch (error: any) {
    console.error('Email send failed:', error);

    res.status(error.status || 500).json({
      error: error.message || 'Internal server error',
    });
  }
}

// Configure function execution
export const config = {
  maxDuration: 30,
};

What’s Next