resend-inbound
Use when receiving emails with Resend - setting up inbound domains, processing email.received webhooks, retrieving email content/attachments, or forwarding received emails.
Install
mkdir -p .claude/skills/resend-inbound && curl -L -o skill.zip "https://mcp.directory/api/skills/download/9137" && unzip -o skill.zip -d .claude/skills/resend-inbound && rm skill.zipInstalls to .claude/skills/resend-inbound
About this skill
Receive Emails with Resend
Overview
Resend processes incoming emails for your domain and sends webhook events to your endpoint. Webhooks contain metadata only - you must call separate APIs to retrieve email body and attachments.
SDK Version Requirements
This skill requires Resend SDK features for webhook verification (webhooks.verify()) and email receiving (emails.receiving.get()). Always install the latest SDK version. If the project already has a Resend SDK installed, check the version and upgrade if needed.
| Language | Package | Min Version |
|---|---|---|
| Node.js | resend | >= 6.9.2 |
| Python | resend | >= 2.21.0 |
| Go | resend-go/v3 | >= 3.1.0 |
| Ruby | resend | >= 1.0.0 |
| PHP | resend/resend-php | >= 1.1.0 |
| Rust | resend-rs | >= 0.20.0 |
| Java | resend-java | >= 4.11.0 |
| .NET | Resend | >= 0.2.1 |
See send-email skill's installation guide for full installation commands.
Quick Start
- Configure receiving domain - Use Resend's
.resend.appdomain or add MX record for custom domain - Set up webhook - Subscribe to
email.receivedevent - Retrieve content - Call Receiving API for body, Attachments API for files
Domain Setup
Option 1: Resend-Managed Domain (Fastest)
Use your auto-generated address: <anything>@<your-id>.resend.app
No DNS configuration needed. Find your address in Dashboard → Emails → Receiving → "Receiving address".
Option 2: Custom Domain
Add MX record to receive at <anything>@yourdomain.com.
| Setting | Value |
|---|---|
| Type | MX |
| Host | Your domain or subdomain |
| Value | Provided in Resend dashboard |
| Priority | 10 (lowest number wins a conflict, but typically only multiples of 10 are used) |
Critical: Your MX record must have the lowest priority value, or emails won't route to Resend.
Subdomain Recommendation
If you already have MX records (e.g., Google Workspace, Microsoft 365):
| Approach | Result |
|---|---|
| Use subdomain (recommended) | support.acme.com → Resend, acme.com → existing provider |
| Use root domain | All email routes to Resend (breaks existing email) |
# Example: receive at support.acme.com without affecting acme.com
support.acme.com. MX 10 <resend-mx-value>
If you set up Resend to receive email on a root domain, all traffic will be routed to Resend, not to any other mailbox. It's crucial, then, to use a subdomain with inbound emails.
Webhook Setup
Subscribe to email.received
Dashboard → Webhooks → Add Webhook → Select email.received
For local development, use tunneling (ngrok, VS Code Port Forwarding):
ngrok http 3000
# Use https://abc123.ngrok.io/api/webhook as endpoint
Webhook Payload Structure
Important: Payload contains metadata only, not email body or attachment content.
{
"type": "email.received",
"created_at": "2024-02-22T23:41:12.126Z",
"data": {
"email_id": "a1b2c3d4-...",
"from": "[email protected]",
"to": ["[email protected]"],
"cc": [],
"bcc": [],
"subject": "Question about my order",
"attachments": [
{
"id": "att_abc123",
"filename": "receipt.pdf",
"content_type": "application/pdf"
}
]
}
}
Verify Webhook Signatures
Always verify signatures to prevent spoofed events:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function POST(req: Request) {
const payload = await req.text();
const event = resend.webhooks.verify({
payload,
headers: {
'svix-id': req.headers.get('svix-id'),
'svix-timestamp': req.headers.get('svix-timestamp'),
'svix-signature': req.headers.get('svix-signature'),
},
secret: process.env.RESEND_WEBHOOK_SECRET,
});
if (event.type === 'email.received') {
// Process the email
}
return new Response('OK', { status: 200 });
}
Retrieving Email Content
Webhooks exclude email body and headers. Call the Receiving API to get them:
if (event.type === 'email.received') {
const { data: email } = await resend.emails.receiving.get(
event.data.email_id
);
console.log(email.html); // HTML body
console.log(email.text); // Plain text body
console.log(email.headers); // Email headers
}
Why this design? Serverless environments have request body size limits. Separating content retrieval supports large emails and attachments.
Handling Attachments
Get Attachment Metadata and Download URLs
const { data: attachments } = await resend.emails.receiving.attachments.list({
emailId: event.data.email_id,
});
for (const attachment of attachments) {
console.log(attachment.filename);
console.log(attachment.download_url); // Valid for 1 hour
console.log(attachment.expires_at);
}
Download Attachment Content
const response = await fetch(attachment.download_url);
const buffer = await response.arrayBuffer();
// Save to storage, process, etc.
await saveToStorage(attachment.filename, buffer);
Important: download_url expires after 1 hour. Call the API again for a fresh URL if needed.
Forwarding Emails
Complete workflow to receive and forward an email with attachments:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function POST(req: Request) {
const payload = await req.text();
const event = resend.webhooks.verify({ /* ... */ });
if (event.type === 'email.received') {
// 1. Get email content
const { data: email } = await resend.emails.receiving.get(
event.data.email_id
);
// 2. Get attachments (if any)
const { data: attachmentList } = await resend.emails.receiving.attachments.list({
emailId: event.data.email_id,
});
// 3. Download and encode attachments
const attachments = await Promise.all(
attachmentList.map(async (att) => {
const res = await fetch(att.download_url);
const buffer = Buffer.from(await res.arrayBuffer());
return {
filename: att.filename,
content: buffer.toString('base64'),
};
})
);
// 4. Forward the email
await resend.emails.send({
from: 'Support System <[email protected]>',
to: ['[email protected]'],
subject: `Fwd: ${email.subject}`,
html: email.html,
text: email.text,
attachments,
});
}
return new Response('OK', { status: 200 });
}
Routing by Recipient
All emails to your domain arrive at the same webhook. Route based on the to field:
if (event.type === 'email.received') {
const recipient = event.data.to[0];
if (recipient.includes('support@')) {
await handleSupportEmail(event.data);
} else if (recipient.includes('billing@')) {
await handleBillingEmail(event.data);
} else {
await handleUnknownEmail(event.data);
}
}
Common Mistakes
| Mistake | Fix |
|---|---|
| Expecting body in webhook payload | Webhook has metadata only - call resend.emails.receiving.get() for body |
| MX record not lowest priority | Ensure Resend's MX has lowest number (highest priority) |
| Adding MX to root domain with existing email | Use subdomain to avoid breaking existing email service |
| Using expired download_url | URLs expire after 1 hour - call attachments API again for fresh URL |
| Not verifying webhook signatures | Always verify — unverified events can't be trusted |
| Forgetting to return 200 OK | Resend retries on non-200 responses |
Storage Note
Resend stores received emails even if:
- Webhook isn't configured yet
- Webhook endpoint is down
View all received emails in Dashboard → Emails → Receiving tab.
More by openclaw
View all skills by openclaw →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
pdf-to-markdown
aliceisjustplaying
Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.
Related MCP Servers
Browse all serversConnect your free email services or Mailchimp for seamless automation. Manage emails efficiently with IMAP and SMTP supp
AI-powered email composition and delivery via Resend API. Customize sender, reply-to, schedule. Integrate easily with Se
Rtfmbro is an MCP server for config management tools—get real-time, version-specific docs from GitHub for Python, Node.j
Send transactional emails easily using Mailtrap Email API. Reliable email delivery, flexible features, and secure integr
Mailtrap Email API enables seamless sending of transactional emails. Alternative to SendGrid API or temp mail API. Try r
Mailgun MCP Server lets AI assistants send emails via the Mailgun API and view email delivery analytics for seamless AI
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.