404. Contact Sendara before building a production workflow that depends on them.Publish the MX record that Sendara gives you. It points at the Amazon SES receiving endpoint for the region, inbound-smtp.<region>.amazonaws.com, at priority 10. SES then receives every email to that domain and hands it to Sendara. Sendara parses it into fields (from, to, subject, text, HTML, attachments) and delivers it to you in two ways: a real-time email.received webhook, and a GET /v1/inbound pull API.
Enable a domain
Receiving is enabled on a verified domain. Enabling returns the MX record to publish at your DNS provider.
inbound.yourdomain.com instead so your existing mail is untouched.curl -X POST https://api.sendara.dev/v1/domains/dom_abc123/inbound \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "enabled": true }'{ "enabled": true, "mx_record": { "type": "MX", "priority": "10", "value": "inbound-smtp.us-east-1.amazonaws.com" } }Prefer zero setup? Every account also has a hosted test inbox at <account_id>@inbound.sendara.dev. Email it from anywhere to see receiving work with no DNS changes.
Mail inboxes
Sendara Mail can host multiple inbox addresses on the same verified domain. Create addresses like [email protected], [email protected], and [email protected], then filter the Mail app, threads API, and search API by that inbox. This is separate from routing rules: inboxes organize mail inside Sendara, while routes forward matched mail to another system.
inbox_id; messages to other addresses on the same domain still land in the account-level mailbox.# create [email protected]
curl https://api.sendara.dev/v1/mail/inboxes \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "domain": "mail.acme.com", "local_part": "support" }'
# list configured inboxes
curl https://api.sendara.dev/v1/mail/inboxes \
-H "Authorization: Bearer sk_live_xxx"
# read one inbox view
curl "https://api.sendara.dev/v1/mail/threads?inbox=support%40mail.acme.com" \
-H "Authorization: Bearer sk_live_xxx"
# search inside that inbox
curl "https://api.sendara.dev/v1/mail/search?q=invoice&inbox=support%40mail.acme.com" \
-H "Authorization: Bearer sk_live_xxx"Replies from a thread default to the address that received the latest inbound message when that domain is verified for sending. You can also pass from explicitly to POST /v1/mail/compose or POST /v1/mail/threads/{id}/reply.
Search and export mail
Mail search accepts normal text plus operators for the cases where generic inbox search usually falls short. Use from:, to:, subject:, has:attachment, after:YYYY-MM-DD, and before:YYYY-MM-DD. The same filters work on exports, so users can take a precise slice of their mailbox with them.
# search a single role inbox
curl "https://api.sendara.dev/v1/mail/search?q=from%3Aalice%40example.com%20has%3Aattachment&inbox=support%40mail.acme.com" \
-H "Authorization: Bearer sk_live_xxx"
# export that same slice as JSON
curl "https://api.sendara.dev/v1/mail/export?format=json&q=from%3Aalice%40example.com%20has%3Aattachment&inbox=support%40mail.acme.com" \
-H "Authorization: Bearer sk_live_xxx" \
-o support-export.json
# export as mbox for mailbox import tools
curl "https://api.sendara.dev/v1/mail/export?format=mbox&inbox=support%40mail.acme.com" \
-H "Authorization: Bearer sk_live_xxx" \
-o support.mboxThe email.received webhook
Subscribe a webhook to the email.received event. Each received message is delivered as a signed POST (verified exactly like every other Sendara webhook). Like every event, it arrives wrapped in the standard envelope. Top-level keys are event_id / event_type, and the inbound email lives under payload. The example below is abbreviated. The full payload also carries cc, headers, size_bytes, thread_id, inbox_id, rfc_message_id, in_reply_to, references_ids, normalized_subject, and an s3_key on each attachment.
{
"event_id": "evt_inb_9f21",
"event_type": "email.received",
"message_id": "inb_9f21",
"account_id": "acc_abc123",
"payload": {
"id": "inb_9f21",
"domain": "inbound.yourdomain.com",
"recipient": "[email protected]",
"inbox_id": "inbox_a1b2c3",
"from": "Alice <[email protected]>",
"to": ["[email protected]"],
"cc": [],
"subject": "Need help with my order",
"text": "Hi, my order hasn't arrived…",
"html": "<p>Hi, my order hasn't arrived…</p>",
"spf_verdict": "PASS",
"dkim_verdict": "PASS",
"dmarc_verdict": "PASS",
"spam_verdict": "PASS",
"virus_verdict": "PASS",
"size_bytes": 84992,
"thread_id": "thr_5c10",
"attachments": [{ "filename": "receipt.pdf", "content_type": "application/pdf", "size": 84211, "s3_key": "inbound/inb_9f21/att/0" }],
"received_at": "2026-06-17T10:02:00Z"
},
"occurred_at": "2026-06-17T10:02:00Z",
"created_at": "2026-06-17T10:02:01Z"
}spf_verdict, dkim_verdict, dmarc_verdict, spam_verdict and virus_verdict. Sendara drops a message whose virus_verdict is FAIL, so it never reaches you. Sendara does not drop a message on a spam verdict. It delivers the message and gives you the verdict to act on.Pull API
List and fetch received mail over the API. This is useful for backfill or if you would rather poll than run a webhook.
# list recent inbound emails (bodies omitted)
curl https://api.sendara.dev/v1/inbound \
-H "Authorization: Bearer sk_live_xxx"
# fetch one with full body + attachments metadata
curl https://api.sendara.dev/v1/inbound/inb_9f21 \
-H "Authorization: Bearer sk_live_xxx"
# download an attachment by index
curl https://api.sendara.dev/v1/inbound/inb_9f21/attachments/0 \
-H "Authorization: Bearer sk_live_xxx" -o receipt.pdf
# fetch the original, unparsed RFC 822 message
curl https://api.sendara.dev/v1/inbound/inb_9f21/raw \
-H "Authorization: Bearer sk_live_xxx" -o message.emlThe routing rules
By default Sendara fires email.received to your subscribed webhooks for every received message. A routing rule forwards one address to its own destination. The destination is a webhook (support@ to your ticketing system) or an email address (a real inbox). Set destination_type to webhook or to email. You create, list, update (PUT /v1/inbound/routes/{id}) and delete a route over the API.
# forward support@ to a webhook (durable: signed, retried, logged)
curl https://api.sendara.dev/v1/inbound/routes \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"domain": "inbound.yourdomain.com",
"match_prefix": "support",
"destination_type": "webhook",
"forward_url": "https://app.yourcompany.com/hooks/inbound"
}'
# forward everything else to a real inbox
curl https://api.sendara.dev/v1/inbound/routes \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"domain": "inbound.yourdomain.com",
"match_prefix": "*",
"destination_type": "email",
"forward_address": "[email protected]"
}'Sendara delivers a webhook route forward through the same durable path as every other Sendara webhook. Sendara retries it with a growing delay, records it in your delivery history, and signs it. The signing scheme is the same as the scheme for an email.received subscription. It is a bare hex Sendara-Signature header and a Sendara-Timestamp header, over the same envelope. The SDK verifyWebhook helper therefore works for a route forward and for a subscription.
import { verifyWebhook, WebhookVerificationError } from "sendara";
// Express handler. The raw request body is required to verify the signature.
app.post("/hooks/inbound", express.raw({ type: "*/*" }), (req, res) => {
try {
// Verifies the Sendara-Signature / Sendara-Timestamp headers and returns the
// parsed event. Works for email.received subscriptions AND route forwards.
const event = verifyWebhook(req.body, req.headers, process.env.SENDARA_WEBHOOK_SECRET!);
if (event.event_type === "email.received") {
const email = event.payload; // parsed inbound email: from, subject, text, html, thread_id…
// …append it to a conversation thread keyed by email.thread_id / in_reply_to…
}
res.sendStatus(200);
} catch (err) {
if (err instanceof WebhookVerificationError) return res.status(400).send("bad signature");
throw err;
}
});HMAC-SHA256(signing_secret, "<Sendara-Timestamp>." + rawBody). Hex-encode the result. Then compare it to Sendara-Signature in constant time. The steps are the same for a subscription and for a route forward.Common questions
- Will the MX record break my existing email?
- It can. An MX record routes all mail for that domain to Sendara. Enable a subdomain such as inbound.yourdomain.com instead. Your existing mail at the apex then stays with your current provider.
- Can I try receiving before I change any DNS?
- Yes. Every account has a hosted test inbox at <account_id>@inbound.sendara.dev. Send mail to it from anywhere, and the message arrives on the same webhook and the same pull API.
- Does Sendara filter spam and viruses?
- Amazon SES scans every inbound message. Sendara puts the verdicts on the event as spf_verdict, dkim_verdict, dmarc_verdict, spam_verdict and virus_verdict. Sendara drops a message whose virus_verdict is FAIL. Sendara delivers a message with a spam verdict, and gives you the verdict to act on.
- What is the difference between an inbox and a route?
- An inbox organizes mail inside Sendara, and it puts an inbox_id on the message. A route forwards a matched address to a webhook or to another email address. When you create an inbox, the domain still accepts mail sent to every other address.
- Can I read the original message?
- Yes. Call GET /v1/inbound/{id}/raw for the unparsed RFC 822 message. Call GET /v1/inbound/{id}/attachments/{index} for one attachment. Both routes take a read key.