Inbound Email
Receive incoming emails via webhook with custom inbound routing domains
Inbound routing lets you receive incoming emails and forward their contents to your application via webhook. Point an MX record at MisarMail and every email sent to that address triggers a POST to your webhook URL.
Authentication
All inbound endpoints require an API key with the inbound scope (inbound:read for GET, inbound:write for POST/DELETE, or admin). Base URL: https://api.misar.io/mail.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /v1/inbound | List inbound domains |
POST | /v1/inbound | Create an inbound domain |
DELETE | /v1/inbound | Remove an inbound domain (id in body) |
Setup Overview
Create an inbound domain via POST /mail/v1/inbound with your domain, subdomain, and HTTPS webhook_url. The response returns a generated webhook_secret and DNS instructions.
Add an MX record for <subdomain>.inbound.mail.misar.io pointing to mail.misar.io with priority 10.
Receive emails — every inbound message triggers a signed POST to your webhook_url.
List inbound domains
/mail/v1/inboundList all inbound routing domains for the authenticated account.
Response fields
successbooleantrue when the request succeeded.
dataArray<InboundDomain>The inbound domains. Each includes id, domain, subdomain, webhook_url, is_verified, mx_verified, status, created_at, and updated_at.
curl https://api.misar.io/mail/v1/inbound \
-H "Authorization: Bearer msk_YOUR_API_KEY"{
"success": true,
"data": [
{
"id": "9f1c8a2e-4d3b-4c1a-8f7e-2b6d5a1c9e0f",
"domain": "yourdomain.com",
"subdomain": "replies",
"webhook_url": "https://yourapp.com/webhooks/email",
"is_verified": true,
"mx_verified": true,
"status": "active",
"created_at": "2026-03-01T10:00:00Z",
"updated_at": "2026-03-01T10:05:00Z"
}
]
}Create an inbound domain
/mail/v1/inboundCreate a new inbound routing domain. The server generates a webhook_secret for you and returns it in the response — store it to verify inbound payload signatures.
Request body
domainstring (3–253 chars)bodyrequiredRoot domain you own, e.g. yourdomain.com.
subdomainstring (1–63 chars)bodyrequiredSubdomain label to receive email on, e.g. replies.
webhook_urlstring (https)bodyrequiredHTTPS URL to POST inbound payloads to.
Response fields
successbooleantrue when the domain was created.
dataobjectThe created domain, including id, domain, subdomain, webhook_url, webhook_secret, status, and created_at.
instructionsobjectDNS setup guidance: mx_record (the exact MX record string to publish) and a note.
curl -X POST https://api.misar.io/mail/v1/inbound \
-H "Authorization: Bearer msk_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "yourdomain.com",
"subdomain": "replies",
"webhook_url": "https://yourapp.com/webhooks/email"
}'{
"success": true,
"data": {
"id": "9f1c8a2e-4d3b-4c1a-8f7e-2b6d5a1c9e0f",
"domain": "yourdomain.com",
"subdomain": "replies",
"webhook_url": "https://yourapp.com/webhooks/email",
"webhook_secret": "whsec_3f9a...c21b",
"status": "pending",
"created_at": "2026-03-01T10:00:00Z"
},
"instructions": {
"mx_record": "Add MX record: replies.inbound.mail.misar.io → mail.misar.io (priority 10)",
"note": "DNS changes can take up to 24 hours to propagate."
}
}Remove an inbound domain
/mail/v1/inboundRemove an inbound domain. Pass the domain id in the JSON body. Emails sent to this domain after deletion will bounce. Returns 404 if no domain with that id exists for the account.
Request body
idstring (uuid)bodyrequiredID of the inbound domain to remove.
curl -X DELETE https://api.misar.io/mail/v1/inbound \
-H "Authorization: Bearer msk_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "9f1c8a2e-4d3b-4c1a-8f7e-2b6d5a1c9e0f" }'{ "success": true }Webhook Payload
When an email is received, MisarMail sends a POST request to your webhook_url with a flat JSON payload:
{
"event": "email.received",
"id": "msg_inb_xyz789",
"from": "sender@example.com",
"to": "replies@yourdomain.com",
"cc": "team@yourdomain.com",
"subject": "Re: Your order has shipped",
"text": "Thanks for the update!",
"html": "<p>Thanks for the update!</p>",
"headers": {
"message-id": "<abc123@example.com>",
"in-reply-to": "<def456@mail.misar.io>"
},
"attachments": [
{
"filename": "invoice.pdf",
"contentType": "application/pdf",
"size": 48210,
"contentId": "<part1.abc@example.com>"
}
],
"spam_score": 0.2,
"message_id": "<abc123@example.com>",
"received_at": "2026-04-06T12:00:00Z"
}from and to are plain address strings, not objects. cc and contentId are optional. Attachment metadata is delivered inline (filename, contentType, size, optional contentId) — there is no separate download URL.
Webhook Signature Verification
Every payload is signed with HMAC-SHA256 using your webhook_secret. The signature is sent in the X-MisarMail-Signature header, prefixed with sha256=. Strip the prefix before comparing:
import crypto from 'crypto';
function verifyInboundWebhook(
rawBody: string,
header: string, // value of X-MisarMail-Signature, e.g. "sha256=abc123..."
secret: string
): boolean {
const signature = header.replace(/^sha256=/, '');
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Always verify the webhook signature before processing inbound payloads to prevent spoofing. Reject any payload that fails verification with a 403 response.