Logo

REST API (Send Email)

This guide explains how to send messages and trigger tracking using MonkeysMail’s public API. It covers authentication, scopes, request/response formats, error handling, quotas, and examples in cURL, Node.js, PHP, and Python.

1) Authentication

Send your API key in the header:

X-API-Key: pk_live_abc123.7f9e… (prefix.secret)
  • Format: prefix.secret
  • Scopes: required per endpoint (see below). Accepted aliases & wildcards are supported, e.g. mail, messages, mail:*, messages.*, or *.
  • Binding: Keys are bound to a Company and a Domain. Requests using keys without a company/domain will be rejected (403).

Required scopes per endpoint

EndpointScope
POST /messages/sendmail:send
POST /messages/send/listmail:send:list
POST /messages/send/segmentmail:send:segment

The platform also accepts scope aliases (messages:*, etc.). If a key lacks the scope you’ll receive 403 forbidden with { required: "..." }.

2) Sending: One‑off (explicit recipients)

Endpoint: POST smtp.monkeysmail.com/messages/send

Query/body mode:

  • Default is enqueue (asynchronous queue).
  • To force immediate send, pass mode=sync as a query param or { "mode": "sync" } in JSON body or { "runNow": true }.

Request body

{
	"from": { "email": "no-reply@example.com", "name": "Example Inc." },
	"to": ["user1@example.com", "user2@example.com"],
	"cc": ["cc@example.com"],
	"bcc": ["audit@example.com"],
	"subject": "Welcome to Example",
	"text": "Hello! This is the text part.",
	"html": "<h1>Hello!</h1><p>This is the HTML part.</p>",
	"reply_to": "support@example.com",
	"headers": { "X-Campaign": "onboarding" },
	"tags": ["onboarding", "welcome"],
	"metadata": { "userId": "u_123", "plan": "pro" },
	"template_id": "tpl_welcome_v3",
	"variables": { "firstName": "Jane" },
	"attachments": [
		{
			"filename": "terms.pdf",
			"content": "<base64>",
			"content_type": "application/pdf"
		}
	]
}

Notes

  • At least one of to/cc/bcc must contain a valid email (duplicates are deduped case‑insensitively).
  • attachments[].content must be base64.
  • headers are forwarded as custom message headers (prefix with X- when appropriate).
  • metadata is stored for search & webhooks/exports (if enabled).
  • If you use templates, include template_id and variables.

Responses

Enqueue (default)

{
	"mode": "enqueue",
	"status": "queued",
	"recipients": 2,
	"message": "queued"
}

Status: 202 Accepted (or 200, 201, 503 depending on internal state). Possible status values: queued, preview, queue_failed, sent.

Sync (immediate)

{
	"mode": "sync",
	"company": 42,
	"domain": 7,
	"at": "2025-09-23T22:10:00+00:00",
	"id": "msg_...",
	"status": "sent",
	"accepted": ["user1@example.com"],
	"rejected": []
}

Status mirrors delivery attempt (commonly 200/201).

Validation error (example)

{
	"error": "invalid_request",
	"message": "At least one recipient (to/cc/bcc) is required",
	"field": "to/cc/bcc"
}

3) Sending to a List (server‑side recipient resolution)

Endpoint: POST smtp.monkeysmail.com/messages/send/list

Provide a list hash (64‑char hex, typically sha256) in the JSON body.

Request

{
	"listHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"from": { "email": "campaigns@example.com", "name": "Example" },
	"subject": "September newsletter",
	"html": "<html>…</html>",
	"text": "…",
	"tags": ["newsletter"],
	"metadata": { "edition": "2025-09" }
}

Response

{
	"status": "queued",
	"target": { "type": "list", "hash": "aaaaaaaa…" },
	"recipientsTotal": 1200,
	"recipientsQueued": 1200,
	"recipientsFailed": 0,
	"details": [{ "to": "alice@example.com", "status": "queued" }],
	"detailsTruncated": true
}
  • Status is 202 when everything queued; if mixed results you may get 207 Multi-Status with partial failures.

4) Sending to a Segment (server‑side recipient resolution)

Endpoint: POST smtp.monkeysmail.com/messages/send/segment

Provide a segment hash (64‑char hex) in the JSON body.

Request

{
	"segmentHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
	"from": { "email": "offers@example.com", "name": "Example" },
	"subject": "VIP Offer",
	"html": "<html>…</html>",
	"text": "…"
}

Response

{
	"status": "queued",
	"recipients": 347,
	"target": { "type": "segment", "hash": "bbbbbbbb…" },
	"message": "queued"
}

5) Tracking

These endpoints record events related to a unique RID (recipient token) embedded by MonkeysMail.

Open tracking (pixel)

  • GET/HEAD smtp.monkeysmail.com/t/o/{rid}.gif
  • Returns a 1×1 GIF. HEAD also records an open (useful for privacy‑preserving proxies).

Example <img> tag in HTML:

<img src="smtp.monkeysmail.com/t/o/${RID}.gif" width="1" height="1" alt="" style="display:none" />

Click tracking (redirect)

  • GET smtp.monkeysmail.com/t/c/{rid}?u={b64url(target)}
  • u is the URL‑safe base64 of the final destination.

Example link rewrite:

<a href="smtp.monkeysmail.com/t/c/${RID}?u=${B64URL('https://example.com/pricing')}">Pricing</a>

Unsubscribe handler

  • GET smtp.monkeysmail.com/t/u/{rid}?reason={text}
  • Records an unsubscribe (or reason) and returns a simple HTML confirmation page. You can customize your templates to point the unsubscribe footer here.

6) Quotas & Rate Limiting

MonkeysMail enforces message quotas per plan and time window:

  • Starter: window = day, limit = 150 message units/day
  • Higher tiers: window = month, limit = plan.includedMessages (or unlimited)

When a request exceeds the remaining allowance you’ll get HTTP 429:

{
	"error": "rate_limited",
	"reason": "Message quota exceeded for your plan",
	"window": "day",
	"limit": 150,
	"remaining": 0,
	"resetAt": "2025-09-24T00:00:00+00:00"
}

Unit accounting: The API counts unique valid recipient emails across to/cc/bcc in a request or the resolved set for lists/segments.

7) Common Errors

HTTPerrormessageNotes
401unauthorizedInvalid or missing API keyMissing/invalid X-API-Key
403forbiddenAPI key is missing the required scopeIncludes { required: "…" }
422invalid_requestField‑specific reasone.g., no recipients
429rate_limitedQuota exceededSee payload above
500internal_errorInternal Server ErrorLog correlation on server

8) Code Examples

cURL (one‑off send, enqueue)

curl -X POST "smtp.monkeysmail.com/messages/send" \
	-H "Content-Type: application/json" \
	-H "X-API-Key: pk_live_abc123.7f9e..." \
	-d '{
		"from": {"email": "no-reply@example.com", "name": "Example"},
		"to": ["user@example.com"],
		"subject": "Hello",
		"text": "Hi there",
		"html": "<p>Hi there</p>",
		"tags": ["hello"]
}'

Node (fetch) – sync send

import fetch from "node-fetch";

const res = await fetch(`${process.env.API_BASE}/messages/send?mode=sync`, {
method: "POST",
	headers: {
	"Content-Type": "application/json",
	"X-API-Key": process.env.MONKEYSMAIL_API_KEY
	},
	body: JSON.stringify({
		from: { email: "no-reply@example.com", name: "Example" },
		to: ["user@example.com"],
		subject: "Hello",
		html: "<p>Hi</p>",
	})
});
const data = await res.json();
console.log(res.status, data);

PHP (cURL) – list send

$body = [
	'listHash' => 'aaaaaaaa...64hex...',
	'from' => ['email' => 'campaigns@example.com', 'name' => 'Example'],
	'subject' => 'September',
	'html' => '<h1>Hi</h1>'
];
$ch = curl_init("smtp.monkeysmail.com/messages/send/list");
curl_setopt_array($ch, [
	CURLOPT_POST => true,
	CURLOPT_HTTPHEADER => [
		'Content-Type: application/json',
		'X-API-Key: ' . getenv('MONKEYSMAIL_API_KEY')
	],
	CURLOPT_POSTFIELDS => json_encode($body),
	CURLOPT_RETURNTRANSFER => true,
]);
$out = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Python (requests) – segment send

import os, base64, requests
API_BASE = os.environ.get('API_BASE')
KEY = os.environ.get('MONKEYSMAIL_API_KEY')

payload = {
	"segmentHash": "bbbbbbbb...64hex...",
	"from": {"email": "offers@example.com", "name": "Example"},
	"subject": "VIP Offer",
	"html": "<p>Hi VIP</p>"
}
res = requests.post(f"smtp.monkeysmail.com/messages/send/segment", json=payload, headers={
	"X-API-Key": KEY
})
print(res.status_code, res.json())

9) Best Practices

  • Use enqueue for bulk sends; reserve sync for small, latency‑sensitive messages.
  • Retry only on safe statuses (e.g., HTTP 429 after resetAt, transient 503).
  • Normalize recipients (lowercase emails). The API already dedupes, but client‑side hygiene helps.
  • Tag and metadata your sends for analytics and later segmentation.
  • Templates + variables improve consistency; keep your unsubscribe/footer links aligned with the tracking endpoints.
  • Clicks: wrap in tracking URLs when you build custom HTML. If you use the platform’s template helpers, link rewriting may be handled automatically.

10) Field Reference (JSON)

FieldTypeRequiredNotes
from.emailstringSender email (must be verified for the domain)
from.namestring Display name
tostring[]one ofAny of to/cc/bcc must include at least one valid email
ccstring[]  
bccstring[]  
subjectstring 
textstring  
htmlstring  
reply_tostring  
headersobject Custom headers
tagsstring[] Up to your conventions
metadataobject Free‑form JSON
template_idstring Use with templated sends
variablesobject Merge variables for templates
attachments[].filenamestring  
attachments[].contentbase64  
attachments[].content_typestring MIME type
modestring sync to send immediately (or runNow: true)
listHashstring✓ (list)64‑char hex
segmentHashstring✓ (segment)64‑char hex

11) Security & Compliance

  • Key handling: keep API keys server‑side; never expose in client browsers or mobile apps.
  • Domain binding: your key is tied to a verified sending domain; use matching from addresses.
  • PII: store only necessary metadata. Avoid sensitive data.
  • Unsubscribe: include the unsubscribe link in bulk/marketing emails.
  • DMARC/MTA‑STS/TLS‑RPT: ensure your domain DNS is configured for best deliverability (see platform docs).

12) Glossary

  • RID: recipient‑specific tracking token used to link opens/clicks/unsubscribes to a message + email address.
  • Queued: accepted for processing; delivery happens asynchronously.
  • Sync: send immediately in the request cycle (use sparingly).