How to receive emails with Resend
Resend added inbound email in 2025. This guide walks through the MX record, the email.received webhook, fetching the body with the Receiving API, replying in the same thread, and what the 30-day retention means for you.
How Resend receiving works
Receiving in Resend is domain-wide and webhook-driven:
- You enable Receiving on a verified domain and publish one MX record. Any address on the domain (hello@, support@, anything@) is accepted. There are no per-address routes.
- Each incoming message creates an
email.receivedwebhook event with metadata:email_id,from,to,subject,message_id, and attachment info. - Your code calls the Receiving API (
GET /emails/receiving/{id}) for the HTML, plain text, and headers, and downloads attachments through temporary signed URLs. - Received messages are also listed read-only under Emails → Receiving in the dashboard.
Set up receiving on your domain
- Verify the domain for sending first (SPF and DKIM records). Receiving is enabled per domain after verification.
- Open Domains → your domain in the Resend dashboard and switch on Receiving.
- Add the MX record Resend shows at your DNS provider. The value is specific to your domain and region, so copy it from the dashboard rather than from a tutorial.
- Give it the lowest priority value on the domain. If the domain already has MX records for Google Workspace or Microsoft 365, use a subdomain such as
mail.yourdomain.cominstead of the root. - Click “I've added the record” and send a test email to any address on the domain.
<id>.resend.app address immediately.Handle the email.received webhook
- Create a webhook (dashboard or
POST /webhooks) subscribed toemail.received. Store thesigning_secretit returns. - Verify every request with the
svix-id,svix-timestamp, andsvix-signatureheaders against the raw body. Retries reuse the samesvix-id, so de-duplicate on it. - Fetch the content with
GET /emails/receiving/{email_id}, then savehtml,text,headers, andmessage_id. - For each attachment, call the attachments endpoint to get a download URL and store the file yourself. The URLs expire.
// Minimal handler (Node)
const event = resend.webhooks.verify({ payload: rawBody, headers, webhookSecret });
if (event.type === 'email.received') {
const { data } = await resend.emails.receiving.get(event.data.email_id);
// data.html, data.text, data.headers, data.message_id, data.attachments[]
}Reply to a received email in the same thread
There is no reply button in the dashboard. To reply, send a new email through the API from an address on your domain and set the threading headers:
In-Reply-To: themessage_idof the received emailReferences: all earlier message ids in the conversation, space separated- Subject prefixed with
Re:so mail clients group the thread
Keep the original message_id per conversation in your database, or follow-up replies will break out of the thread.
Retention, quotas, and limits
| Item | Resend |
|---|---|
| Data retention | 30 days on Free, Pro, and Scale; flexible on Enterprise |
| Inbound pricing | Included on all plans; each received email counts toward the plan quota |
| Free plan quota | 3,000 emails per month, 100 per day (sent + received) |
| Address matching | Domain catch-all; filter by recipient in your code |
| Body in webhook | No; fetch via Receiving API |
Compare this with Mailgun, where stored messages last only 3 days and inbound is driven by routes: can Mailgun receive emails?
A simpler way: give the domain an inbox
If the goal is for a person to read and answer mail on hello@ or support@, you do not need to build any of the above. Inboxes connects to your Resend account with an API key, creates the webhook, stores every received message with threads and attachments, and lets you reply from your domain with the threading headers set correctly. The only manual step left is enabling Receiving and adding the MX record in Resend. See Resend webmail: where do received emails go?
Frequently asked questions
Yes. Verify your domain, turn on Receiving for it in the Resend dashboard, and add the MX record Resend shows. From then on, every address on that domain is received and each message fires an email.received webhook. You can also receive without DNS changes on a Resend-managed *.resend.app address.
No. The email.received payload carries metadata only (email_id, from, to, subject, message_id, attachment list). You fetch the HTML, text, and headers with GET /emails/receiving/{id}, and attachments through temporary download URLs.
No. Once the MX record is in place, Resend receives mail for any address on the domain. There are no routes to configure, which also means you filter by recipient in your own code.
Resend lists 30 days of data retention on the Free, Pro, and Scale plans. If you need history beyond that, store the content yourself or use an inbox client that keeps a synced copy.
No. Inbound is included on every plan and each received email counts toward the same quota as a sent email (3,000 per month on Free, with a 100 per day limit).