MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarDevMisarCoderMisarSEOMisar PlatformMisar SSO
API Reference

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

MethodPathDescription
GET/v1/inboundList inbound domains
POST/v1/inboundCreate an inbound domain
DELETE/v1/inboundRemove 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

GET/mail/v1/inbound

List all inbound routing domains for the authenticated account.

Response fields

successboolean

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

Request
curl https://api.misar.io/mail/v1/inbound \
  -H "Authorization: Bearer msk_YOUR_API_KEY"
200 — OK
{
  "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

POST/mail/v1/inbound

Create 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)bodyrequired

Root domain you own, e.g. yourdomain.com.

subdomainstring (1–63 chars)bodyrequired

Subdomain label to receive email on, e.g. replies.

webhook_urlstring (https)bodyrequired

HTTPS URL to POST inbound payloads to.

Response fields

successboolean

true when the domain was created.

dataobject

The created domain, including id, domain, subdomain, webhook_url, webhook_secret, status, and created_at.

instructionsobject

DNS setup guidance: mx_record (the exact MX record string to publish) and a note.

Request
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"
  }'
201 — Created
{
  "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

DELETE/mail/v1/inbound

Remove 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)bodyrequired

ID of the inbound domain to remove.

Request
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" }'
200 — OK
{ "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.