Your Vite app needs an email backend, not an exposed API key. The browser asks; your server sends.
Add a small authenticated endpoint in the backend you already run, queue the message there, and return an accepted ID without putting recipient or credential logic in the client bundle.
✓A client bundle cannot hold an API key. Send from your own backend.
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 / 03 ]Integration shape
Treat Vite as the client build tool it is.
Use a small server endpoint beside your Vite app
Vite does not provide a production server runtime. Put sending in your API, serverless function, or full-stack backend.
Publish product events
Publish signup, trial, and payment events from the backend that validates them, not from browser analytics.
Verify the webhook
Webhooks need a server endpoint; a static Vite app has nowhere to receive one.
[ 02 / 03 ]Server quickstart
Queue one Vite-triggered email from your backend.
The client calls your authenticated endpoint; the backend holds the key, sets the idempotency key, and returns the message ID.
Vite browser code must call your own server endpoint; the project key never belongs in the client bundle.
export async function sendWelcome(email: string, userId: string) {
return 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" }),
});
}Accepted response
{ "id": "msg_7ca31", "status": "queued" }[ 03 / 03 ]Related
Keep building with Vite
Questions people ask us
Put a safe email boundary behind your Vite app.
Build one authenticated endpoint, queue a message, and keep the key out of the bundle.