DEV Community

Elastic Email
Elastic Email

Posted on

How to Send Email from Cloudflare Workers, Vercel Edge & Supabase Edge with the Elastic Email SDK + 12 Serverless Examples

![Elastic Email real-world email integration examples for developers and AI agents](...)

TL;DR

Sending email from a serverless function with the Elastic Email SDK is the same call you'd make on Node, with one exception. On edge runtimes (Cloudflare Workers, Vercel Edge, Netlify Edge, Deno Deploy, Supabase Edge Functions) you pass a fetch-based axios instance as the third argument to EmailsApi, because those runtimes have no node:http. The working code for 12 platforms lives in one MIT-licensed folder, and every project exposes the same three endpoints.

Why does the SDK fail on edge runtimes?

@elasticemail/elasticemail-client-ts-axios is built on axios, and axios reaches for Node's http adapter unless told otherwise. Cloudflare Workers, Vercel Edge, Netlify Edge, Deno Deploy, and Supabase Edge Functions don't ship node:http, so the first call fails at bundle time or at runtime, depending on your bundler. On Node runtimes (AWS Lambda, Railway, Encore, Vercel's Node functions, Firebase, Azure Functions, Cloud Run, Convex Node actions) the default adapter is fine.

The fix: one argument

import axios from "axios";
import { Configuration, EmailsApi } from "@elasticemail/elasticemail-client-ts-axios";

const config = new Configuration({ apiKey: process.env.ELASTICEMAIL_API_KEY });

// Only needed on runtimes without node:http
const emailsApi = new EmailsApi(config, undefined, axios.create({ adapter: "fetch" }));

const { data } = await emailsApi.emailsTransactionalPost({
  Recipients: { To: [to] },
  Content: {
    From: process.env.EMAIL_FROM!, // must be on a domain verified in Elastic Email
    Subject: subject,
    Body: [{ ContentType: "HTML", Content: `<p>${message}</p>` }],
  },
});

// data.TransactionID, data.MessageID
Enter fullscreen mode Exit fullscreen mode

Keep your top-level axios dependency on the same range the SDK uses (~1.18.0) so npm dedupes it. Two copies of axios in the tree are the second most common reason a serverless send fails.

Deploy to Cloudflare Workers

cd cloudflare-workers
npm install
wrangler login
wrangler secret put ELASTICEMAIL_API_KEY
wrangler secret put ELASTICEMAIL_WEBHOOK_TOKEN
npm run deploy
Enter fullscreen mode Exit fullscreen mode

On Workers, environment variables are not globals. They arrive as the second argument of fetch(request, env), so the example builds EmailsApi per request. nodejs_compat is enabled in wrangler.toml so axios' Node imports resolve at bundle time.

Test it

curl $URL/health
Enter fullscreen mode Exit fullscreen mode
curl -X POST $URL/send \
  -H "Content-Type: application/json" \
  -d '{"to":"you@yourdomain.com","subject":"Hello","message":"Hi from the edge!"}'
Enter fullscreen mode Exit fullscreen mode

Register the webhook after you deploy.

When you save a webhook, Elastic Email sends one test event to the URL and only saves the webhook if it gets a 2xx, so deploy first. Requests are not signed; the examples append a shared token to the callback URL and verify it with a constant-time comparison. Events arrive as GET requests with the details in the query string (status, to, transaction, messageid).

The other 11 platforms:

Platform Runtime The one thing to know
Vercel Functions Node and Edge api/send.ts runs on Node, api/send-edge.ts on Edge; files starting with _ are not deployed
Supabase Edge Functions Deno No npm install, functions import npm: specifiers; set verify_jwt = false for the webhook; an auth-hook function delivers Supabase Auth emails through Elastic Email
AWS Lambda (SAM) Node The key is a NoEcho CloudFormation parameter; move it to Secrets Manager or SSM for production
Deno Deploy Deno deno task deploy
Netlify Functions Node netlify env:import .env
Railway Node A long-running Hono server, not a function; binds 0.0.0.0:$PORT
Encore.ts Node Secrets are read with secret("ElasticEmailApiKey")(), not process.env
Firebase Cloud Functions Node defineSecret + .value(); a callable sendEmailToMe for Flutter and mobile apps so the API key never ships in the app
Azure Functions Node v4 model /send requires the function key; the webhook stays anonymous behind ?token=
Google Cloud Run Node in a container The runtime service account needs roles/secretmanager.secretAccessor on both secrets
Convex Convex runtime + Node The SDK call lives in a "use node" internal action that convex/http.ts invokes with ctx.runAction

FAQ

Do I need a verified domain to test?

Yes. The From address must be on a domain verified in Elastic Email; publishing the SPF and DKIM records takes a few minutes.

Is there a free tier?

Yes. 100 emails a day, no credit card. Every example in the repository runs on it.

Can I use SMTP instead of the SDK?

Yes. Point your tool at smtp.elasticemail.com with SMTP credentials (Settings > SMTP in the dashboard, not the API key).

The repo has a separate folder with 28 SMTP integrations, from Supabase Auth to WordPress to Grafana.


Repository: github.com/ElasticEmail/elasticemail-examples

Something missing? Issues and pull requests are open, and so are the comments below.

Top comments (0)