Add a new expert to the Remotion experts page
日本語の概要は準備中です。原文の説明を表示しています。
Use when receiving emails with Resend - setting up inbound domains, processing email.received webhooks, retrieving email content/attachments, or forwarding received emails. Triggers on "inbound email", "receive email", "email webhook", "Resend inbound", "process incoming email", "email forwarding", "email.received", "MX records for email".
インストールする前に、エージェントに与えられる指示の中身を確認できます。
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.
This skill is context-independent — it does not use brand/ files and works identically in any project.
Output: A webhook handler file with signature verification, content retrieval, and routing logic.
.resend.app domain or add MX record for custom domainemail.received eventUse your auto-generated address: <anything>@<your-id>.resend.app
No DNS configuration needed. Find your address in Dashboard → Emails → Receiving → "Receiving address".
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.
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.
email.receivedDashboard → 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
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": "sender@example.com",
"to": ["support@acme.com"],
"cc": [],
"bcc": [],
"subject": "Question about my order",
"attachments": [
{
"id": "att_abc123",
"filename": "receipt.pdf",
"content_type": "application/pdf"
}
]
}
}
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 });
}
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.
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);
}
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.
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 <system@acme.com>',
to: ['team@acme.com'],
subject: `Fwd: ${email.subject}`,
html: email.html,
text: email.text,
attachments,
});
}
return new Response('OK', { status: 200 });
}
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);
}
}
| Failure | Action |
|---|---|
| Webhook signature verification fails | Return 400, log the attempt. Never process unverified webhooks. |
resend.emails.receiving.get() returns error | Log the email_id, return 200 to acknowledge webhook, queue for retry via your own retry logic. |
Attachment download_url expired | Call resend.emails.receiving.attachments.list() again for a fresh URL. |
| Attachment download times out | Retry with exponential backoff (max 3 attempts). Log failure if all retries exhaust. |
| Malformed email (missing from/subject) | Log and skip gracefully. Return 200 to prevent Resend retries on bad data. |
| 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 — attackers can send fake events |
| Forgetting to return 200 OK | Resend retries on non-200 responses |
Resend stores received emails even if:
View all received emails in Dashboard → Emails → Receiving tab.
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Add a new expert to the Remotion experts page
日本語の概要は準備中です。原文の説明を表示しています。
Add a new sound effect to @remotion/sfx
日本語の概要は準備中です。原文の説明を表示しています。
Use when setting up a secure email inbox for any AI agent — configuring inbound email via Resend, webhooks, tunneling for local development, and implementing security measures to prevent prompt injection attacks. Also use when someone mentions 'agent email', 'bot inbox', 'receive emails for agent', 'agent webhook', 'email security for AI', 'prompt injection via email', 'inbound email for bot', or wants their AI agent to receive and respond to emails securely.
日本語の概要は準備中です。原文の説明を表示しています。
Builds, measures, and improves an Answer Engine Optimization program across ChatGPT, Perplexity, Claude, Gemini, Google AI Overviews, and Copilot. Use for AEO, GEO, LLMO, AI search visibility, AI citations, brand mentions, answer-engine monitoring, llms.txt, AI crawler audits, or questions like 'what does AI say about us?' and 'why are competitors cited instead of us?'. Produces a durable evidence report and prioritized gameplan, not a one-shot content checklist.
日本語の概要は準備中です。原文の説明を表示しています。
Generate Apple App Store screenshot pages as a Next.js app with html-to-image export at required resolutions. Screenshots are advertisements, not documentation — every screenshot sells one idea. Use when building App Store screenshots, generating exportable marketing screenshots for iOS apps, or creating programmatic screenshot generators. Triggers on 'app store screenshots', 'App Store', 'screenshot generator', 'iOS screenshots', 'marketing screenshots', 'phone mockup', 'ASO screenshots', 'app store assets', 'app listing', 'app store page', or 'app preview images'. Also use when someone is about to submit an app and needs store assets, or when they say 'make my app store page look good'. Includes iPhone and iPad mockup components with pre-measured dimensions.
日本語の概要は準備中です。原文の説明を表示しています。
Build detailed buyer personas and audience profiles from research. Use this skill whenever the user mentions audience, buyer persona, ideal customer, target market, ICP, watering holes, 'who am I selling to', customer research, or audience profile. Also use when content feels unfocused or generic (that's an audience problem), when conversion is low because messaging doesn't resonate, when starting any new project (audience should be first), or when any downstream skill needs audience.md but it doesn't exist yet. Even if the user doesn't explicitly ask for audience research, trigger this if they're writing copy or building a landing page without a clear audience defined. Three approaches: Quick Profile, Persona Build, Community Mining.
日本語の概要は準備中です。原文の説明を表示しています。