─── documentation v1.0

Mail-Reacher API documentation

Send transactional emails through one stable API, route them through the right provider, render MJML templates, track opens and clicks, and audit deliverability with Mail-Check.

Quickstart

Use an environment-scoped API key, call POST /api/emails/send, and let Mail-Reacher route the message through the provider configured for that environment.

curl -X POST https://mail-reacher.com/api/emails/send \
  -H "Authorization: Bearer mr_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "to": [
    {
      "email": "user@example.com",
      "name": "Marie"
    }
  ],
  "from_email": "noreply@your-domain.com",
  "from_name": "Your App",
  "reply_to": "support@your-domain.com",
  "subject": "Welcome to the app",
  "html": "<p>Hello Marie</p>",
  "text": "Hello Marie",
  "tracking": {
    "mode": "off"
  },
  "metadata": {
    "ticket_id": "SUP-1842"
  }
}'

Concepts

Projects

Group domains, environments, senders, templates and logs around a product or workspace.

Environments

Test keys simulate sends inside the internal inbox. Live keys deliver through the configured provider.

Senders

A sender stores provider credentials, default From identity and deliverability settings.

Authentication

Every API request uses a Bearer token. Keys are scoped by environment and permissions, so a staging key cannot accidentally send real email.

Authorization: Bearer demo-token

Read scopes

templates:read can list and read active templates in the key's project. contacts:read can list and read contacts only.

Write scopes

emails:send sends email. contacts:write is required to create, update or delete contacts. Write scopes never grant access to another project.

Send an email

The send endpoint accepts either a template ID or inline HTML. Template defaults can provide the subject and sender when appropriate.

A 201 response confirms asynchronous acceptance, not delivery. Store data.message_id with your support ticket and poll data.status_url using an emails:read key. The first email remains untracked until persistent consent has been recorded. Tracked sends require exactly one recipient; aggregate also requires a non-personal tracking.group_id.

FieldTypeRequiredDescription
tostring|Recipient[]yesrecipient(s) β€” email string or array of { email, name? }
from_emailstringyes*sender email β€” default can come from the configured sender
from_namestringnosender display name
subjectstringyes*subject β€” default can come from the template
template_idintegernotemplate ID to render
htmlstringnoinline HTML when no template is used
textstringnoplain-text body
cc / bccstring[]|Recipient[]nocopy and blind-copy recipients
variablesobjectnovariables injected into the template
reply_tostringnoreply-to address
tracking.modeoff|aggregate|individualnomay lower the environment mode, never raise it
tracking.group_idstringfor aggregatenon-personal cohort identifier, 1 to 128 characters
metadataobjectnoclient data such as ticket_id returned by the status API
tagsstring[]notags used by unsubscribe rules

Response

{
  "data": {
    "message_id": "01J2Z7K3RVZ8N4P5Q6T2W8X9Y",
    "status": "queued",
    "status_url": "https://mail-reacher.com/api/emails/01J2Z7K3RVZ8N4P5Q6T2W8X9Y",
    "recipient_summary": {
      "total": 1,
      "delivered": 0,
      "bounced": 0,
      "failed": 0,
      "pending": 1
    },
    "tracking": {
      "requested_mode": "off",
      "planned_mode": "off",
      "applied_mode": null,
      "phase": "preflight",
      "reason": "tracking_off",
      "warnings": []
    },
    "client_metadata": {
      "ticket_id": "SUP-1842"
    }
  }
}

Record consent before the first tracked email

Call this endpoint from your backend when the person checks the consent box. The public preferences page can only deny or withdraw tracking; it can never grant it.

curl -X PUT https://mail-reacher.com/api/tracking-consents \
  -H "Authorization: Bearer mr_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "user@example.com",
  "status": "granted",
  "max_mode": "aggregate",
  "event_id": "8a3c0e22-3c69-4cf5-aeb1-5db7ba6a6d82",
  "occurred_at": "2026-07-15T08:30:00+00:00",
  "source": "signup_form",
  "expected_version": 0,
  "policy_version": "privacy-2026-07",
  "proof_reference": "signup:usr_123:checkbox_v2"
}'

MJML templates

Create responsive MJML templates, preview them in the dashboard, and inject variables at send time.

<mj-text>
  Hello {{ first_name }},
  your account is ready to <a href="{{ activation_link }}">activate</a>.
</mj-text>

Tracking & events

off keeps every business link direct and adds no pixel. aggregate stores only daily cohort counters, with no per-message or per-recipient engagement history. individual can associate engagement with the message. Both tracked modes require prior consent, and provider-native tracking is always disabled or the send is blocked.

Delivery states and engagement are separate. Templates may use {{ tracking_preferences_url }} or the tracking preferences block. A minimal footer is auto-appended only when tracking is effectively active; an off environment removes the block and injects no Mail-Reacher URL.

Read aggregate statistics from GET /api/email-analytics/{group_id}. Open and click values count events, not unique people. They remain suppressed until 24 hours after the bucket ends and until that bucket contains at least 20 deliveries.

{
  "event_id": "b8cb7f89-0e4f-4a83-8d04-bc40d4fc56d8",
  "event": "email.delivered",
  "occurred_at": "2026-07-15T10:14:32Z",
  "email": {
    "message_id": "01J2Z7K3RVZ8N4P5Q6T2W8X9Y",
    "status_url": "https://mail-reacher.com/api/emails/01J2Z7K3RVZ8N4P5Q6T2W8X9Y",
    "status": "delivered",
    "engagement": null,
    "client_metadata": {
      "ticket_id": "SUP-1842"
    }
  }
}

Mail-Check

Mail-Check audits SPF, DKIM, DMARC, reverse DNS, blacklists, headers, content, TLS, MX and IP signals, then returns a readable score out of 10.

Supported providers

AWS SES

Automatically neutralized through a Configuration Set created and verified by Mail-Reacher. Sending stays blocked until verification succeeds.

Postmark

Disable server tracking in Postmark, then confirm that external setting in the Mail-Reacher environment.

Mailgun

o:tracking, o:tracking-opens and o:tracking-clicks are forced to no on every send.

SendGrid

open_tracking and click_tracking are disabled in tracking_settings on every send.

Brevo

Disable transactional tracking in Brevo and confirm the setting in the environment. Sending is blocked without that confirmation.

Custom SMTP

SMTP cannot control downstream service tracking. Disable it at the provider and confirm the external setting.

SDKs & packages

Use the official packages when you want typed helpers instead of calling the REST API directly. Keep MAILREACHER_API_KEY server-side only: it selects the Mail Reacher environment and therefore the configured provider or simulation mode.

JavaScript / TypeScript

Install the core SDK, or add a framework adapter for existing Next.js, Nodemailer or TanStack Start code.

yarn add @mail-reacher/sdk
yarn add @mail-reacher/next
yarn add @mail-reacher/nodemailer
yarn add @mail-reacher/tanstack-start

PHP / Laravel

Use the framework-agnostic PHP SDK, or the Laravel transport when existing Mail::to(), notifications and mailables should keep working without rewrites.

composer require codiblenet/mail-reacher-php
composer require codiblenet/laravel-mail-reacher

Laravel configuration stays intentionally small: set MAIL_MAILER=mailreacher and MAILREACHER_API_KEY=demo-token. The API endpoint is fixed to mail-reacher.com.

Error codes

Synchronous errors contain error, message and request_id. After a 201, asynchronous failures are exposed as a filtered data.failure object by the status endpoint.

401API key missing, expired, or invalid
402Monthly quota reached
403The key does not have the required scope
404Resource not found (template, contact…)
409Consent version conflict or provider not ready
422Validation or business rule failure
429Rate limit exceeded
500Mail-Reacher server error β€” retry with the request_id

Rate limits

Rate limits are plan- and usage-dependent. Provider errors are normalized and retried when safe. If you need higher throughput, open an in-app support ticket.

Ready to wire your first send?

Create a project, configure a sender, then test safely with a test key before switching to live.

Start free β†’