fetch call that works on AWS Lambda, Vercel, Cloudflare Workers, and any other function runtime, plus the retry and idempotency handling a production function needs.
Using Cursor? Jump straight in using this prompt
Why HTTP instead of SMTP
Serverless functions are short-lived and stateless, and SMTP needs a persistent TCP connection with a multi-step handshake. A single HTTP request completes in milliseconds, holds no connection open between invocations, and needs no queue or database on the function side. Delivery retries to the recipient’s mail server happen inside Lettr, so the function returns as soon as the API accepts the message.Prerequisites
API Key
Create an API key in the Lettr dashboard
Verified Domain
Add and verify your sending domain
Choose your platform
AWS Lambda
Send emails from AWS Lambda functions
Vercel Functions
Integrate with Vercel serverless functions
Cloudflare Workers
Send emails from Cloudflare Workers
Generic example
On any platform, a send is onePOST to the emails endpoint. The request ID lives under data.data in the JSON body:
Complete example with retries and idempotency
Serverless platforms retry failed invocations, and a function that times out mid-request cannot tell whether Lettr accepted the email. TheIdempotency-Key header makes both cases safe. Reusing the same key and payload within 24 hours returns the original result instead of sending a second email, so the key has to be stable across retries and across invocations. Derive it from the business event, not from the attempt:
Retry-After when present and otherwise sleeps a random interval under a capped exponential ceiling, so many clients recovering from the same outage do not retry in lockstep:
Using the Lettr SDK
On platforms with npm support, the Node.js SDK wraps the same call:Environment variables
Every platform supports environment variables. The examples above read these two:- AWS Lambda: AWS Secrets Manager or Lambda environment variables
- Vercel: the dashboard or
vercel env add - Cloudflare Workers:
wrangler secret put
Serverless-specific practices
Function timeout. A send call normally completes in under a second, but the retry loop above caps each request at 30 seconds and waits through several backoff intervals. Set the function timeout to cover the worst case rather than the happy path:fetch beats an HTTP client dependency:
data.data.request_id on every send. It is the handle for the message in webhooks and the Events API.
Common patterns
Event-triggered emails
A function that reacts to an application event (signup, purchase) picks the content by event type. The idempotency key combines the event type with the entity so a redelivered event does not send twice:Batch processing
One invocation can fan out several sends in parallel. Each recipient gets its own key so a partial failure and re-run only sends to the recipients that did not go through:For sending to many recipients (100+), use batch sending with a single API call, or process in smaller chunks to stay inside the function timeout.
What’s next
Idempotency
Key rules, replay behaviour, and the 24-hour retention window
Errors & Retries
Which responses are safe to retry
API Reference
Complete API documentation