Documentation

Send your first email in two minutes

A typed SDK, a clean REST API, and a first-class MCP server — all sending over Amazon SES. Everything below is copy-paste-correct and exactly what your agent reads.

Get started

Quickstart

Install the SDK, drop in your key, and send. The same call handles transactional and broadcast email — there's no second product to learn.

install
npm install @macromail/sdk
import { MacroMail } from '@macromail/sdk';

const mm = new MacroMail(process.env.MACROMAIL_API_KEY);

const { data, error } = await mm.emails.send({
  from: 'You <hello@yourdomain.com>',
  to: ['user@example.com'],
  subject: 'Welcome to MacroMail',
  html: '<p>It works — sent over Amazon SES.</p>',
});

A successful send returns { data, error } with a status of queued — not delivered. Delivery is confirmed asynchronously via webhooks and the logs.

Security

Authentication

Every request authenticates with a secret API key passed as a bearer token:

Authorization: Bearer mm_live_xxxxxxxxxxxx
mm_live_…Live keys

Send real email to real inboxes over Amazon SES. Treat them like passwords — server-side only, never in client bundles.

mm_test_…Test keys

Run the full validation and queueing pipeline and get back a real id — but nothing is ever delivered. Perfect for CI.

Scopes

Keys carry one of two scopes. A full key can manage domains, audiences, and other keys; a sending key can only send email. Give production services the narrowest scope they need.

auth.ts
import { MacroMail } from '@macromail/sdk';

// Pass your secret key. Live keys send real email; test keys
// run the full pipeline but never hit a real inbox.
const mm = new MacroMail('mm_live_xxxxxxxxxxxxxxxxxxxx');

// Or over the wire — every request is authenticated with a
// bearer token in the Authorization header:
//   Authorization: Bearer mm_live_xxxxxxxxxxxxxxxxxxxx
Core

Sending email

mm.emails.send() (or POST /v1/emails) takes the fields below and returns { data, error }. It never throws on an expected failure — inspect error instead. A successful call returns status queued; delivery is confirmed later.

fromrequired
string

Sender address. Use a display name + a verified domain, e.g. "Acme <hello@acme.com>".

torequired
string | string[]

One or more recipient addresses. Up to 50 per send.

subjectrequired
string

The email subject line.

htmloptional
string

HTML body. Provide html, text, or both — at least one is required.

textoptional
string

Plain-text body. Sent as the text/plain alternative.

ccoptional
string | string[]

Carbon-copy recipients.

bccoptional
string | string[]

Blind carbon-copy recipients.

reply_tooptional
string | string[]

Address replies should be routed to. Defaults to the from address.

tagsoptional
{ name, value }[]

Key/value labels for filtering logs, analytics, and webhooks.

scheduled_atoptional
string (ISO 8601)

Schedule the send for a future time, e.g. "2026-07-01T09:00:00Z". Returns status scheduled.

Setup

Domains & deliverability

Add a domain with POST /v1/domainsand we return the exact records to publish at your DNS provider. We check them live — there's no vanity score. Once all five are green, you can send.

TypeHost / NameValuePurpose
TXT@v=spf1 include:amazonses.com ~allSPF — authorizes Amazon SES to send for your domain.
CNAME<token1>._domainkey.<domain><token1>.dkim.macromail.devDKIM key 1 of 3 — signs your mail so it isn't forged.
CNAME<token2>._domainkey.<domain><token2>.dkim.macromail.devDKIM key 2 of 3.
CNAME<token3>._domainkey.<domain><token3>.dkim.macromail.devDKIM key 3 of 3.
TXT_dmarc.<domain>v=DMARC1; p=none; rua=mailto:dmarc@macromail.devDMARC — start in monitor mode (p=none), tighten once aligned.

The three DKIM tokens are unique per domain and shown in your dashboard when you add it. Keep DMARC at p=none until your reports look clean, then move to quarantine and reject.

Reference

API reference

A small, predictable REST surface. Base URL https://api.macromail.dev. Every response is JSON and every endpoint is authenticated.

MethodEndpointPurpose
POST/v1/emailsSend a single transactional email.
GET/v1/emailsList recent emails with their delivery status.
GET/v1/emails/:idRetrieve one email and its full lifecycle timeline.
POST/v1/emails/batchSend up to 100 emails in a single request.
POST/v1/domainsAdd a sending domain and get its DNS records.
POST/v1/domains/:id/verifyTrigger a live re-check of a domain's DNS.
POST/v1/api-keysCreate a scoped API key (full or sending-only).
POST/v1/audiencesCreate an audience to hold contacts.
POST/v1/contactsAdd or upsert a contact in an audience.
POST/v1/broadcastsSend a broadcast to an audience.
POST/v1/webhooksRegister an endpoint for signed delivery events.

Example — send an email

A send returns the new email's id and a queued status immediately.

POST /v1/emails
// Request — POST /v1/emails
{
  "from": "Acme <hello@acme.com>",
  "to": ["user@example.com"],
  "subject": "Welcome to Acme",
  "html": "<p>You're in.</p>"
}

// Response — 200 OK
{
  "id": "em_2b1f9c7a4d3e",
  "status": "queued",
  "created_at": "2026-06-28T17:04:11Z"
}
Reference

Errors

Errors come back in the error half of { data, error } with a stable name, an HTTP status, and a human-readable message. Branch on the name, not the prose.

CodeStatusCauseFix
authentication_error401Missing, malformed, or revoked API key.Send Authorization: Bearer <key>. Rotate the key if it was leaked.
permission_error403The key's scope doesn't allow this action.Use a key with the full scope, or call a sending-only endpoint.
domain_not_verified422The from domain hasn't passed DNS verification.Add the SPF/DKIM/DMARC records, then POST /v1/domains/:id/verify.
validation_error422A field is missing or malformed (bad address, no body).Read error.message — it names the exact field that failed.
rate_limit_exceeded429Too many requests in the current window.Back off using the Retry-After header; batch where you can.
not_found404No resource with that id, or it isn't on your account.Check the id and that the key belongs to the right project.
Events

Webhooks

Register an endpoint with POST /v1/webhooks and we'll POST a JSON event for each delivery milestone. Every payload is signed with your webhook secret so you can verify it came from us, and failed deliveries are retried with exponential backoff.

  • email.deliveredThe receiving server accepted the message.
  • email.bouncedPermanent failure — the address is undeliverable.
  • email.complainedThe recipient marked the message as spam.
  • email.openedThe recipient opened the email (when tracking is on).
  • email.clickedA tracked link in the email was clicked.

Verify the MacroMail-Signature header against the raw request body before trusting an event. Return a 2xx quickly; anything else is treated as a failure and retried.

Agent-first

Agents / MCP

MacroMail ships a first-class MCP server. Connect it to Claude, Cursor, or Windsurf and your agent can create API keys, verify domains, and send test email without leaving the editor. Install it in one line:

npx @macromail/mcp install