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-tokenRead 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.
| Field | Type | Required | Description |
|---|---|---|---|
| to | string|Recipient[] | yes | recipient(s) β email string or array of { email, name? } |
| from_email | string | yes* | sender email β default can come from the configured sender |
| from_name | string | no | sender display name |
| subject | string | yes* | subject β default can come from the template |
| template_id | integer | no | template ID to render |
| html | string | no | inline HTML when no template is used |
| text | string | no | plain-text body |
| cc / bcc | string[]|Recipient[] | no | copy and blind-copy recipients |
| variables | object | no | variables injected into the template |
| reply_to | string | no | reply-to address |
| tracking.mode | off|aggregate|individual | no | may lower the environment mode, never raise it |
| tracking.group_id | string | for aggregate | non-personal cohort identifier, 1 to 128 characters |
| metadata | object | no | client data such as ticket_id returned by the status API |
| tags | string[] | no | tags 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-startPHP / 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.
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 β