Send emails with Nuxt
Transactional and marketing email from a Nitro server route. Your key stays in private runtime config, never in the browser.
✓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 Nuxt?
Create a key, keep it on the server, and call one endpoint from a Nitro server route.
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
Declare the key in private runtimeConfig and set NUXT_AEM_KEY=aem_… in your environment. Never put it under runtimeConfig.public.
nuxt.config.tsexport default defineNuxtConfig({ runtimeConfig: { aemKey: "", // set by NUXT_AEM_KEY }, });Send the email
A Nitro route reads the key from runtime config and posts to the email API. The Idempotency-Key header stops a repeated request from sending twice.
server/api/welcome.post.tsexport default defineEventHandler(async (event) => { const { email, userId } = await readBody(event); const config = useRuntimeConfig(event); return $fetch("https://sendandretain.com/api/v1/emails", { method: "POST", headers: { Authorization: `Bearer ${config.aemKey}`, "Idempotency-Key": `welcome-${userId}` }, body: { to: email, template: "welcome" }, }); });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 Nuxt app?
Nitro server routes
A defineEventHandler route checks the browser's input and keeps the key in private runtime config.
Events from the server
Send product events from the server route that saves the change, not from a composable in the browser.
A raw body for webhooks
Use readRawBody(event) so the signature check sees the original bytes.
[ 03 / 05 ]Marketing email
Can I send marketing emails from Nuxt too?
Yes. Send an event from the same Nitro route 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 Nuxt?
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 Nuxt signup route using Send & Retain, with an idempotency key.”
Order receipt
“Add an order receipt email to our Nitro checkout route, keeping the API key in private runtime config.”
Webhook
“Add a Nitro route that verifies Send & Retain delivery webhooks with readRawBody and flags bounced users.”
Trial automation
“Send a trial_started event from our Nuxt app and build a paused trial-conversion automation for it.”
[ 05 / 05 ]Related
Keep building with Nuxt
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.
SvelteKit
Send emails from SvelteKit server routes and form actions.
Questions people ask us
Send your first email from Nuxt today.
Create a key, paste the code into a Nitro server route, and watch the email land in the message log.