Every dashboard action is an MCP tool your agent can call.Read the docs

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.

Server quickstart
Vite

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" }

RuntimeServer process
RequestPOST /api/v1/emails
Result202 · queued
Credentials stay server-side

[ 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.

  1. 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.

  2. 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/.env
    AEM_KEY=aem_…
  3. 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.ts
    export 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();
    }
  4. 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.”

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.