Send Email
Send transactional and marketing emails via POST /v1/send
Send Email
Base URL: https://api.misar.io/mail or https://api.misar.io/mail
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": "[email protected]", "name": "My App" },
"to": [{ "email": "[email protected]", "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.misar.io/mail/v1/send \
-H "Authorization: Bearer msk_your_key_here" \
-H "Content-Type: application/json" \
-H "X-MisarMail-Sandbox: true" \
-d '{
"from": { "email": "[email protected]", "name": "Misar" },
"to": [{ "email": "[email protected]" }],
"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.misar.io/mail/v1/send \
-H "Authorization: Bearer msk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"from": { "email": "[email protected]", "name": "Misar" },
"to": [{ "email": "[email protected]", "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.misar.io/mail/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: "[email protected]", name: "Misar" },
to: [{ email: "[email protected]", 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.misar.io/mail/v1/send",
headers={
"Authorization": f"Bearer {os.environ['MISARMAIL_API_KEY']}",
"Content-Type": "application/json",
"X-Source-App": "MyApp",
},
json={
"from": {"email": "[email protected]", "name": "Misar"},
"to": [{"email": "[email protected]", "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.