API Reference
Everything you need to send emails, manage contacts, handle suppressions, and track delivery — all via a simple REST API.
All API requests use Basic Auth. The username is always api and the password is your API key.
| Username | api |
| Password | key-2e1f7a7032011faf... |
Base URL:
https://api.monkeysmail.com/api/v2/{domain}Replace {domain} with your verified sending domain (e.g. mail.yourcompany.com).
curl -u "api:key-xxxxxxxxxxxxxxxxxxxx" \
https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages| Endpoint | Limit |
|---|---|
| Send single | 100 req/min |
| Send batch | 10 req/min |
| Other endpoints | 1000 req/min |
POST /api/v2/{domain}/messages
curl -X POST https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages \
-u "api:key-xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@mail.yourcompany.com",
"to": "user@example.com",
"subject": "Welcome aboard!",
"text": "Hi there! Thanks for signing up."
}'curl -X POST https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages \
-u "api:key-xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"from": "notifications@mail.yourcompany.com",
"to": [
{"email": "alice@example.com", "name": "Alice"},
{"email": "bob@example.com", "name": "Bob"}
],
"subject": "Your Monthly Report",
"html": "<h1>Monthly Report</h1><p>Hi {{name}}, here is your report.</p>",
"reply_to": "support@yourcompany.com",
"tags": ["monthly-report", "may-2026"],
"metadata": { "campaign_id": "camp_456", "user_segment": "premium" }
}'const API_KEY = 'key-xxxxxxxxxxxxxxxxxxxx';
const DOMAIN = 'mail.yourcompany.com';
const BASE = `https://api.monkeysmail.com/api/v2/${DOMAIN}`;
const res = await fetch(`${BASE}/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa(`api:${API_KEY}`),
},
body: JSON.stringify({
from: `hello@${DOMAIN}`,
to: 'user@example.com',
subject: 'Hello from Node!',
html: '<h1>It works!</h1>',
}),
});
const data = await res.json();
// { data: { id: 'msg_a1b2c3...', message: 'Queued. Thank you.' } }import requests
API_KEY = "key-xxxxxxxxxxxxxxxxxxxx"
DOMAIN = "mail.yourcompany.com"
BASE = f"https://api.monkeysmail.com/api/v2/{DOMAIN}"
response = requests.post(
f"{BASE}/messages",
auth=("api", API_KEY),
json={
"from": f"hello@{DOMAIN}",
"to": "user@example.com",
"subject": "Hello from Python!",
"html": "<h1>It works!</h1>",
},
)
print(response.json())
# {'data': {'id': 'msg_...', 'message': 'Queued. Thank you.'}}{
"data": {
"id": "msg_a1b2c3d4e5f60000_abc123",
"message": "Queued. Thank you."
}
}Send up to 1,000 recipients in a single call with per-recipient variables.
POST /api/v2/{domain}/messages/batch
curl -X POST https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages/batch \
-u "api:key-xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"from": "team@mail.yourcompany.com",
"subject": "Hello {{first_name}}!",
"html": "<p>Hi {{first_name}}, your code is <strong>{{code}}</strong>.</p>",
"tags": ["onboarding"],
"recipients": [
{ "email": "alice@example.com", "variables": {"first_name": "Alice", "code": "ALICE10"} },
{ "email": "bob@example.com", "variables": {"first_name": "Bob", "code": "BOB20"} },
{ "email": "carol@example.com", "variables": {"first_name": "Carol", "code": "CAROL15"} }
]
}'{
"data": {
"message": "Batch queued",
"count": 3,
"ids": ["msg_aaa111_abc123", "msg_bbb222_abc123", "msg_ccc333_abc123"]
}
}Create, update, list, and subscribe contacts to mailing lists.
POST /api/v2/{domain}/contacts
curl -X POST https://api.monkeysmail.com/api/v2/mail.yourcompany.com/contacts \
-u "api:key-xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"first_name": "Alice",
"last_name": "Smith",
"status": "subscribed",
"locale": "en",
"timezone": "America/New_York",
"consent_source": "api",
"list_ids": [1, 3]
}'GET /api/v2/{domain}/contacts?page=1&limit=50
curl https://api.monkeysmail.com/api/v2/mail.yourcompany.com/contacts?limit=10 \
-u "api:key-xxxxxxxxxxxxxxxxxxxx"{
"data": [
{
"id": 1,
"email": "alice@example.com",
"first_name": "Alice",
"last_name": "Smith",
"status": "subscribed",
"locale": "en",
"list_ids": [1, 3],
"created_at": "2026-05-08T14:30:00+00:00"
}
],
"meta": { "page": 1, "limit": 10, "total": 42 }
}PUT /api/v2/{domain}/contacts/{id}
curl -X PUT https://api.monkeysmail.com/api/v2/mail.yourcompany.com/contacts/42 \
-u "api:key-xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Alice Updated",
"status": "unsubscribed",
"list_ids": [1]
}'⚠️ list_ids is a full replacement — passing [1] removes the contact from all other lists.
curl -X DELETE https://api.monkeysmail.com/api/v2/mail.yourcompany.com/contacts/42 \
-u "api:key-xxxxxxxxxxxxxxxxxxxx"POST /api/v2/{domain}/contacts/import
curl -X POST https://api.monkeysmail.com/api/v2/mail.yourcompany.com/contacts/import \
-u "api:key-xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"list_ids": [1, 3],
"contacts": [
{"email": "alice@example.com", "first_name": "Alice", "status": "subscribed"},
{"email": "bob@example.com", "first_name": "Bob", "status": "subscribed"},
{"email": "carol@example.com", "first_name": "Carol", "locale": "es"}
]
}'Emails on the suppression list are automatically blocked from sending. Types: bounce, complaint, unsubscribe, manual.
curl -X POST https://api.monkeysmail.com/api/v2/mail.yourcompany.com/suppressions \
-u "api:key-xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"email": "block-me@example.com",
"type": "manual",
"reason": "User requested removal from all lists"
}'curl https://api.monkeysmail.com/api/v2/mail.yourcompany.com/suppressions/check/user@example.com \
-u "api:key-xxxxxxxxxxxxxxxxxxxx"{ "data": { "email": "user@example.com", "suppressed": false } }curl -X DELETE https://api.monkeysmail.com/api/v2/mail.yourcompany.com/suppressions/user@example.com \
-u "api:key-xxxxxxxxxxxxxxxxxxxx"curl -X POST https://api.monkeysmail.com/api/v2/mail.yourcompany.com/suppressions/import \
-u "api:key-xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: text/csv" \
--data-binary @suppressions.csvReceive real-time notifications for every email event. All payloads are HMAC-signed, automatically retried with exponential backoff, and replayable from the dashboard.
deliveredopenedclickedbouncedcomplainedunsubscribedfaileddeferredscheduled{
"event": "delivered",
"timestamp": "2026-05-08T14:30:02+00:00",
"data": {
"message_id": "msg_a1b2c3d4e5f60000_abc123",
"from": "hello@mail.yourcompany.com",
"to": "user@example.com",
"subject": "Welcome aboard!",
"tags": ["welcome"],
"metadata": { "user_id": "42" },
"delivery": {
"status": "delivered",
"mx_host": "aspmx.l.google.com",
"response": "250 2.0.0 OK",
"attempt": 1
}
},
"signature": "sha256=a1b2c3d4e5f6..."
}Every webhook includes an HMAC-SHA256 signature in the X-MonkeysMail-Signature header. Verify it with your webhook signing secret:
import crypto from 'crypto';
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
const isValid = verifyWebhook(rawBody, req.headers['x-monkeysmail-signature'], WEBHOOK_SECRET);
if (!isValid) return res.status(401).end();import hmac, hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)
# In your webhook handler:
is_valid = verify_webhook(request.body, request.headers['X-MonkeysMail-Signature'], WEBHOOK_SECRET)
if not is_valid:
return Response(status=401)💡 Webhooks are retried up to 5 times with exponential backoff (1s, 5s, 30s, 2min, 10min). Respond with a 2xx status within 10 seconds to acknowledge receipt.
GET /api/v2/{domain}/messages?limit=25&offset=0
curl https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages?limit=10 \
-u "api:key-xxxxxxxxxxxxxxxxxxxx"curl https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages/msg_a1b2c3d4e5f60000_abc123 \
-u "api:key-xxxxxxxxxxxxxxxxxxxx"{
"data": {
"id": "msg_a1b2c3d4e5f60000_abc123",
"from": "hello@mail.yourcompany.com",
"to": [{"email": "user@example.com"}],
"subject": "Welcome aboard!",
"status": "delivered",
"tags": ["welcome"],
"sent_at": "2026-05-08T14:30:01+00:00",
"delivered_at": "2026-05-08T14:30:02+00:00"
}
}| Status | Description |
|---|---|
| queued | Accepted, waiting to be sent |
| sending | Being transmitted to the MTA |
| delivered | Successfully delivered |
| bounced | Delivery failed (hard or soft) |
| rejected | Blocked by suppression list |
| failed | System error during send |
All errors return the same structure:
{
"error": "Short error code",
"message": "Human-readable explanation"
}| Code | Error | Description |
|---|---|---|
| 401 | Auth required | Missing or invalid API key |
| 403 | Domain not active | Verification not completed |
| 404 | Not found | Domain doesn't exist or isn't yours |
| 422 | Missing fields | from, to, or subject is missing |
| 400 | Batch limit | More than 1,000 recipients |
| 429 | Rate limited | Too many requests |