Send emails with Vite
Vite builds your frontend, so the send runs in a small backend beside it. Your key stays out of the browser bundle.
✓Free to start. Unlimited contacts on every tier.
One request, complete trace
Request
POST /api/v1/emails
Authorization: Bearer aem_…
Idempotency-Key: welcome-8f21
{ "to": "[email protected]", "template": "welcome" }
202 Accepted
{ "id": "msg_7ca31", "status": "queued" }
[ 01 / 05 ]Quickstart
How do I send email from Vite?
Create a key, keep it on the server, and call one endpoint from a backend endpoint beside your Vite app.
Get an API key
Open Settings → API & MCP and create a key with the Send + manage permission (the default). It starts with aem_ and is shown once.
Store it as a server-side environment variable
Set the key in your backend's environment only. Never use a VITE_ prefix: Vite bundles those into the client for anyone to read.
server/.envAEM_KEY=aem_…Send the email
Your backend posts to the email API, and the browser calls your backend. The Idempotency-Key header makes retries safe.
server/send-welcome.tsexport async function sendWelcome(email: string, userId: string) { const res = await fetch("https://sendandretain.com/api/v1/emails", { method: "POST", headers: { Authorization: `Bearer ${process.env.AEM_KEY}`, "Content-Type": "application/json", "Idempotency-Key": `welcome-${userId}` }, body: JSON.stringify({ to: email, template: "welcome" }), }); if (!res.ok) throw new Error(`Send failed: ${res.status} ${await res.text()}`); return res.json(); }Check the response
A 202 means queued, not delivered. Keep the id, then follow delivery in the message log or through a signed webhook.
202 Accepted{ "id": "msg_7ca31", "status": "queued" }
[ 02 / 05 ]Where it goes
Where should email code live in your Vite app?
A small server endpoint
Vite has no production server. Put the send in your API, a serverless function, or a full-stack backend.
Events from the backend
Send signup, trial, and payment events from the backend that checks them, not from browser analytics.
Somewhere to receive webhooks
Delivery webhooks need a server URL. A static Vite site has nowhere to receive one.
[ 03 / 05 ]Marketing email
Can I send marketing emails from Vite too?
Yes. Send an event from your backend when a user signs up, starts a trial, or goes quiet, and an automation sends the right email. Automations start paused, and every email goes to one person: no newsletter blasts.
- Transactional and marketing email share one contact record and one unsubscribe list.
- Unsubscribes, bounces, and complaints are suppressed for you before every send.
- Templates are react.email components; published versions never change under you.
[ 04 / 05 ]Example prompts
What can you ask your coding agent to do in Vite?
Claude Code, Cursor, and Codex can do these over the Send & Retain MCP server, with your repo open. Automations they build stay paused until you turn them on.
Welcome email
“Add a welcome email to our signup endpoint using Send & Retain, with an idempotency key. Keep the key out of the Vite bundle.”
Contact form
“Create a serverless function our Vite app can call to send a contact-form confirmation email through Send & Retain.”
Key audit
“Check this repo for any email API key that ends up in the Vite client bundle and move it server-side.”
Trial automation
“Send a trial_started event from our Vite app's backend and build a paused trial-conversion automation for it.”
[ 05 / 05 ]Related
Keep building with Vite
Node.js
Send emails from any Node.js or TypeScript server with fetch.
Email API
One REST API for every send, with scoped keys and signed webhooks.
Transactional email
Receipts, resets, and alerts on the same contact record as your automations.
Express
A small Express backend is a common partner for a Vite frontend.
Questions people ask us
Send your first email from Vite today.
Create a key, paste the code into a backend endpoint beside your Vite app, and watch the email land in the message log.