Developers
An email API that your marketing team can also use
Send app email from Handlebars templates your team designs visually. Manage contacts, get signed webhooks, and connect AI agents over MCP. One API key, and the same price as your campaigns.
Transactional email
Send a license key in one call
Templates are designed in the visual builder or written in HTML. The subject and body take the same {{variables}}; optional sections render only when your data has them.
curl https://app.luneamail.com/api/v1/send \
-H "Authorization: Bearer $LUNEAMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateId": "YOUR_TEMPLATE_ID",
"to": "buyer@company.com",
"subject": "Your {{product}} license",
"data": {
"firstName": "Ana",
"product": "Pro",
"licenseKey": "ABCD-1234-EFGH",
"seats": 5
}
}'REST API
Endpoints
Base URL https://app.luneamail.com/api/v1. JSON in, JSON out, Bearer API key.
| Method | Endpoint | What it does |
|---|---|---|
| POST | /send | Send one transactional email from a template, with data for its {{variables}}. |
| POST | /contacts | Add or update a contact, optionally on lists. New contacts need consent.confirmed. |
| GET | /contacts?email= | Look a contact up, with its attributes and lists. |
| DELETE | /contacts?email= | Erase a contact and its events (GDPR). Its unsubscribe record stays. |
| POST | /contacts/unsubscribe | Opt an address out of marketing email, even before it's a contact. |
| GET | /lists | Your lists with member counts. |
curl https://app.luneamail.com/api/v1/contacts \
-H "Authorization: Bearer $LUNEAMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "ana@company.com",
"firstName": "Ana",
"attributes": { "plan": "trial" },
"lists": ["YOUR_LIST_ID"],
"consent": { "confirmed": true, "source": "signup form" }
}'Webhooks
Keep your CRM and app in sync
Signed with HMAC-SHA256, retried with backoff for about two days, and switched off with an email to your admins if your endpoint keeps failing. Endpoints must be public HTTPS URLs.
contact.subscribedA new subscribed contact, from a form, import, the API, an agent or an opt-back-in.contact.unsubscribedSomeone unsubscribed, from an email, the preference page, the API or the app.contact.bouncedAn address hard-bounced and won't be emailed again.contact.complainedSomeone marked an email as spam.campaign.sentA campaign started sending, now or on schedule.campaign.completedA campaign finished, with its totals.
import crypto from "node:crypto";
// rawBody: the request body exactly as received
export function verify(rawBody, headers, secret) {
const ts = headers["x-luneamail-timestamp"];
const expected = "v1=" + crypto
.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`)
.digest("hex");
const got = headers["x-luneamail-signature"] ?? "";
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
return fresh && got.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
}MCP connector
Let AI agents work in your workspace
LuneaMail's MCP server (https://app.luneamail.com/api/mcp) gives agents 12 tools: read your account and results, list audiences and templates, design an email, create a draft and send a test. A real send is two steps: prepare_send returns a summary and a short-lived token, and the agent must show it to you before send_campaign will run.
Read
get_account, get_performance, list_campaigns, get_campaign_results, list_audiences, list_templates
Act
add_contacts, design_email, create_campaign_draft, send_test_email, prepare_send, send_campaign
claude mcp add --transport http luneamail https://app.luneamail.com/api/mcp \
--header "Authorization: Bearer $LUNEAMAIL_API_KEY"Developer questions
How do I authenticate?
Create an API key in Settings › API keys and send it as "Authorization: Bearer <key>". Keys belong to one workspace; only admins can create or revoke them. The base URL is https://app.luneamail.com/api/v1.
Are there rate limits?
Yes: 600 sends a minute and 300 contact requests a minute per API key. Over the limit you get HTTP 429 with a Retry-After header.
Do API emails cost extra?
No. Transactional emails use the same monthly allowance as campaigns. There's no separate transactional product or price.
What happens if a template variable is missing?
If the template uses a variable outside an {{#if}} block and you don't send it, the API refuses with MISSING_VARIABLE instead of sending a broken email. Variables inside {{#if}} or {{#each}} are optional.
Which MCP clients work?
Any MCP client that supports HTTP with a custom Authorization header, such as Claude Code and Cursor. Clients that only support OAuth sign-in can't connect yet.
Get an API key in a minute
Sign up, open Settings › API keys, and send your first email. No card needed.