Receiving
Get replies and other mail for your domain as a webhook, and fetch each message from the API.
Setup
- Add the domain and verify its DKIM record (Domain Verification). The verified record is what proves the domain is yours.
- Point its MX record at
smtp.eml.dev(priority 10), with no other MX records. - Turn on Receiving on the domain's page, or
PATCH /api/v1/domains/:namewith{"domain": {"inbound_enabled": true}}. The MX check runs at once. - Subscribe a webhook to
email.received.
Every address at the domain is received; plus addressing (hello+token@) works as is.
The MX moves all of that name's mail here, so use a subdomain such as
replies.example.com (added and verified as its own domain) to keep your mailbox where it is.
What happens to a message
We check SPF, DKIM, DMARC and ARC while the sender is still connected, add an
Authentication-Results header, and store the message encrypted for a year.
The sender is told it was accepted only once it is stored. Then email.received
fires with the metadata, never the content:
{
"id": "msg_01J...",
"type": "email.received",
"timestamp": "2026-10-16T12:00:00.000Z",
"data": {
"id": 42,
"domain": "replies.example.com",
"from": "Erin <erin@example.org>",
"envelope_sender": "erin@example.org",
"to": ["hello+abc123@replies.example.com"],
"subject": "Re: Your issue",
"message_id": "CAF1...@mail.gmail.com",
"size": 4211,
"auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass" },
"received_at": "2026-10-16T12:00:00.000Z"
}
}
Fetch the content with your API key:
GET /api/v1/inbound_emails/:id returns the headers, the text and HTML bodies and the attachment list;
GET /api/v1/inbound_emails/:id/raw returns the message as received
(message/rfc822), which is what Action Mailbox and other MIME parsers take.
GET /api/v1/inbound_emails lists them.
Treat the content as untrusted: the From header is whatever the sender wrote.
Before you act on who a message is from, check that auth.dmarc is
pass, or use an address only you hand out (a per-recipient token in a plus address).
Never render the HTML body unsanitized.
What is refused
- Mail that fails DMARC where the sender's domain publishes
p=reject(refused permanently). - Messages over 1 MB (refused permanently).
- More than 60 messages a minute, or more than 1 GB a calendar month, per account. The sender is told to try again later, so nothing is lost while you are under the limit again within the sender's retry window.
Each refusal is listed under Inbound in the dashboard and at GET /api/v1/inbound_rejections
for 30 days. Limit hits are counted per domain and hour, and the first one in each hour fires
inbound.limited. Receiving is free.