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.

MethodEndpointWhat it does
POST/sendSend one transactional email from a template, with data for its {{variables}}.
POST/contactsAdd 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/unsubscribeOpt an address out of marketing email, even before it's a contact.
GET/listsYour 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.subscribed A new subscribed contact, from a form, import, the API, an agent or an opt-back-in.
  • contact.unsubscribed Someone unsubscribed, from an email, the preference page, the API or the app.
  • contact.bounced An address hard-bounced and won't be emailed again.
  • contact.complained Someone marked an email as spam.
  • campaign.sent A campaign started sending, now or on schedule.
  • campaign.completed A 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.