Send Email
Send transactional and marketing emails via POST /v1/send
Send Email
Base URL: https://api.mail.misar.io or https://mail.misar.io/api
Endpoint: POST /v1/send
Auth: Authorization: Bearer msk_...
Required scope: send, send:transactional, or send:marketing
Request
Headers
| Header | Required | Value |
|---|---|---|
Authorization | ✓ | Bearer msk_<your_key> |
Content-Type | ✓ | application/json |
X-Source-App | — | Your app name (used in logs) |
X-MisarMail-Sandbox | — | true to send in sandbox mode (not delivered) |
Body
{
"from": { "email": "no-reply@example.com", "name": "My App" },
"to": [{ "email": "user@example.com", "name": "User Name" }],
"subject": "Welcome to My App",
"html": "<h1>Hello!</h1><p>Welcome aboard.</p>",
"text": "Hello! Welcome aboard."
}Fields
| Field | Type | Required | Notes |
|---|---|---|---|
from.email | string | ✓ | Must be a verified account owned by the API key |
from.name | string | — | Display name shown in email client |
to | Array<{email, name?}> | ✓ | Up to 100 recipients |
cc | Array<{email, name?}> | — | Up to 50 CC recipients |
bcc | Array<{email, name?}> | — | Up to 50 BCC recipients |
reply_to | {email, name?} | — | Reply-to address |
subject | string | ✓ | Max 998 characters |
html | string | ✓ (one of) | HTML email body |
text | string | ✓ (one of) | Plain text fallback — both recommended |
alias_id | UUID | — | Route via a specific SMTP pool |
idempotency_key | string | — | Prevent duplicate sends (max 128 chars) |
tags | string[] | — | Up to 10 tags for tracking |
metadata | Record<string, string> | — | Custom key-value data |
Either html or text is required — both are strongly recommended. Many email clients render only plain text.
Responses
200 — Sent
{
"success": true,
"message_id": "msg_abc123",
"provider": "smtp",
"timestamp": "2026-02-17T12:00:00.000Z"
}202 — Queued for Retry
{
"success": false,
"queued": true,
"message": "Email queued for retry"
}The primary SMTP provider was unavailable. The email is automatically retried — no action needed. Do not treat 202 as an error.
422 — Suppressed
{
"success": false,
"suppressed": true,
"error": "Send suppressed — recipient unsubscribed or bounced"
}The recipient is on your suppression list (previously unsubscribed or hard-bounced). The send was intentionally blocked to protect your deliverability. Do not retry — remove the recipient from your send list or check their status via the Contacts API.
Sandbox Mode
Append the X-MisarMail-Sandbox: true header to store the send in the sandbox_sends table without delivering to the real recipient. Useful for integration testing and CI pipelines.
curl https://api.mail.misar.io/v1/send \
-H "Authorization: Bearer msk_your_key_here" \
-H "Content-Type: application/json" \
-H "X-MisarMail-Sandbox: true" \
-d '{
"from": { "email": "no-reply@misar.io", "name": "Misar" },
"to": [{ "email": "user@example.com" }],
"subject": "Sandbox test",
"text": "This will not be delivered."
}'Sandbox response:
{
"success": true,
"message_id": "sandbox_msg_xyz789",
"provider": "sandbox",
"timestamp": "2026-02-17T12:00:00.000Z"
}Requires the sandbox scope on your API key. Sandbox sends count against rate limits but not against your monthly plan quota.
Examples
curl https://api.mail.misar.io/v1/send \
-H "Authorization: Bearer msk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"from": { "email": "no-reply@misar.io", "name": "Misar" },
"to": [{ "email": "user@example.com", "name": "User" }],
"subject": "Welcome to Misar!",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
"text": "Welcome! Thanks for signing up."
}'const res = await fetch("https://api.mail.misar.io/v1/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.MISARMAIL_API_KEY}`,
"Content-Type": "application/json",
"X-Source-App": "MyApp",
},
body: JSON.stringify({
from: { email: "no-reply@misar.io", name: "Misar" },
to: [{ email: "user@example.com", name: "User" }],
subject: "Welcome to Misar!",
html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
text: "Welcome! Thanks for signing up.",
idempotency_key: `welcome-${userId}`,
}),
});
const data = await res.json();
if (!res.ok && !data.queued) {
throw new Error(data.error || "Failed to send email");
}import os, requests
response = requests.post(
"https://api.mail.misar.io/v1/send",
headers={
"Authorization": f"Bearer {os.environ['MISARMAIL_API_KEY']}",
"Content-Type": "application/json",
"X-Source-App": "MyApp",
},
json={
"from": {"email": "no-reply@misar.io", "name": "Misar"},
"to": [{"email": "user@example.com", "name": "User"}],
"subject": "Welcome to Misar!",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
"text": "Welcome! Thanks for signing up.",
"idempotency_key": f"welcome-{user_id}",
},
)
data = response.json()
if not response.ok and not data.get("queued"):
raise Exception(data.get("error", "Failed to send email"))Idempotency
Use idempotency_key to prevent duplicate emails when retrying failed requests:
{
"idempotency_key": "welcome-usr_abc123"
}If a request with the same key was already delivered within 24 hours, the API returns the original result without re-sending:
{
"success": true,
"message_id": "msg_abc123",
"provider": "smtp",
"idempotent": true,
"timestamp": "2026-02-17T12:00:00.000Z"
}Use a unique key per logical send event (e.g., welcome-<userId>, invoice-<invoiceId>).
SMTP Pool Routing
By default, emails are routed through the best available SMTP pool. To force a specific pool (e.g., transactional vs. promotional), pass alias_id:
{
"alias_id": "550e8400-e29b-41d4-a716-446655440000"
}Find alias IDs in Settings → SMTP Pools or via the API Keys settings page.