API Reference

MonkeysMail API Documentation

Everything you need to send emails, manage contacts, handle suppressions, and track delivery — all via a simple REST API.

Authentication

All API requests use Basic Auth. The username is always api and the password is your API key.

Usernameapi
Passwordkey-2e1f7a7032011faf...

Base URL:

text
https://api.monkeysmail.com/api/v2/{domain}

Replace {domain} with your verified sending domain (e.g. mail.yourcompany.com).

bash
curl -u "api:key-xxxxxxxxxxxxxxxxxxxx" \
  https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages

Rate Limits

EndpointLimit
Send single100 req/min
Send batch10 req/min
Other endpoints1000 req/min

Send Email

POST /api/v2/{domain}/messages

Simple text email

bash
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."
  }'

HTML email with tags & metadata

bash
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" }
  }'

Node.js example

javascript
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.' } }

Python example

python
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.'}}

Response — 202 Accepted

json
{
  "data": {
    "id": "msg_a1b2c3d4e5f60000_abc123",
    "message": "Queued. Thank you."
  }
}

Batch Send

Send up to 1,000 recipients in a single call with per-recipient variables.

POST /api/v2/{domain}/messages/batch

bash
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"} }
    ]
  }'
json
{
  "data": {
    "message": "Batch queued",
    "count": 3,
    "ids": ["msg_aaa111_abc123", "msg_bbb222_abc123", "msg_ccc333_abc123"]
  }
}

Contacts

Create, update, list, and subscribe contacts to mailing lists.

Create a contact

POST /api/v2/{domain}/contacts

bash
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]
  }'

List contacts

GET /api/v2/{domain}/contacts?page=1&limit=50

bash
curl https://api.monkeysmail.com/api/v2/mail.yourcompany.com/contacts?limit=10 \
  -u "api:key-xxxxxxxxxxxxxxxxxxxx"
json
{
  "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 }
}

Update a contact

PUT /api/v2/{domain}/contacts/{id}

bash
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.

Delete a contact

bash
curl -X DELETE https://api.monkeysmail.com/api/v2/mail.yourcompany.com/contacts/42 \
  -u "api:key-xxxxxxxxxxxxxxxxxxxx"

Bulk import contacts

POST /api/v2/{domain}/contacts/import

bash
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"}
    ]
  }'

Suppressions

Emails on the suppression list are automatically blocked from sending. Types: bounce, complaint, unsubscribe, manual.

Add a suppression

bash
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"
  }'

Check if suppressed

bash
curl https://api.monkeysmail.com/api/v2/mail.yourcompany.com/suppressions/check/user@example.com \
  -u "api:key-xxxxxxxxxxxxxxxxxxxx"
json
{ "data": { "email": "user@example.com", "suppressed": false } }

Remove a suppression

bash
curl -X DELETE https://api.monkeysmail.com/api/v2/mail.yourcompany.com/suppressions/user@example.com \
  -u "api:key-xxxxxxxxxxxxxxxxxxxx"

Import suppressions (CSV)

bash
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.csv

Webhooks

Receive real-time notifications for every email event. All payloads are HMAC-signed, automatically retried with exponential backoff, and replayable from the dashboard.

Supported events

deliveredopenedclickedbouncedcomplainedunsubscribedfaileddeferredscheduled

Example payload

json
{
  "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..."
}

Verifying signatures

Every webhook includes an HMAC-SHA256 signature in the X-MonkeysMail-Signature header. Verify it with your webhook signing secret:

javascript
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();
python
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.

Retrieve Messages

List messages

GET /api/v2/{domain}/messages?limit=25&offset=0

bash
curl https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages?limit=10 \
  -u "api:key-xxxxxxxxxxxxxxxxxxxx"

Get message details

bash
curl https://api.monkeysmail.com/api/v2/mail.yourcompany.com/messages/msg_a1b2c3d4e5f60000_abc123 \
  -u "api:key-xxxxxxxxxxxxxxxxxxxx"
json
{
  "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"
  }
}

Message statuses

StatusDescription
queuedAccepted, waiting to be sent
sendingBeing transmitted to the MTA
deliveredSuccessfully delivered
bouncedDelivery failed (hard or soft)
rejectedBlocked by suppression list
failedSystem error during send

Error Responses

All errors return the same structure:

json
{
  "error": "Short error code",
  "message": "Human-readable explanation"
}
CodeErrorDescription
401Auth requiredMissing or invalid API key
403Domain not activeVerification not completed
404Not foundDomain doesn't exist or isn't yours
422Missing fieldsfrom, to, or subject is missing
400Batch limitMore than 1,000 recipients
429Rate limitedToo many requests