본문으로 건너뛰기
cccskills
무료GitHub에 공개

resend-inbound

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".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md8.8 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

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.

This skill is context-independent — it does not use brand/ files and works identically in any project.

On Activation

  1. Determine if the user needs a Resend-managed domain or custom domain.
  2. Walk through domain setup and webhook configuration.
  3. Implement webhook handler with signature verification.
  4. Add content retrieval logic (body + attachments as needed).
  5. Add routing logic if multiple recipients are expected.

Output: A webhook handler file with signature verification, content retrieval, and routing logic.

Quick Start

  1. Configure receiving domain — Use Resend's .resend.app domain or add MX record for custom domain
  2. Set up webhook — Subscribe to email.received event
  3. 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.

SettingValue
TypeMX
HostYour domain or subdomain
ValueProvided in Resend dashboard
Priority10 (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):

ApproachResult
Use subdomain (recommended)support.acme.com → Resend, acme.com → existing provider
Use root domainAll 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": "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"
      }
    ]
  }
}

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 <system@acme.com>',
      to: ['team@acme.com'],
      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);
  }
}

Error Handling

FailureAction
Webhook signature verification failsReturn 400, log the attempt. Never process unverified webhooks.
resend.emails.receiving.get() returns errorLog the email_id, return 200 to acknowledge webhook, queue for retry via your own retry logic.
Attachment download_url expiredCall resend.emails.receiving.attachments.list() again for a fresh URL.
Attachment download times outRetry 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.

Anti-Patterns

MistakeFix
Expecting body in webhook payloadWebhook has metadata only — call resend.emails.receiving.get() for body
MX record not lowest priorityEnsure Resend's MX has lowest number (highest priority)
Adding MX to root domain with existing emailUse subdomain to avoid breaking existing email service
Using expired download_urlURLs expire after 1 hour — call attachments API again for fresh URL
Not verifying webhook signaturesAlways verify — attackers can send fake events
Forgetting to return 200 OKResend 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.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Add a new expert to the Remotion experts page

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MoizIbnYousaf/marketing-cli322026년 9월 12일 업데이트

add-sfx

무료

Add a new sound effect to @remotion/sfx

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MoizIbnYousaf/marketing-cli322026년 9월 12일 업데이트

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.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MoizIbnYousaf/marketing-cli322026년 9월 12일 업데이트

ai-seo

무료

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.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MoizIbnYousaf/marketing-cli322026년 9월 12일 업데이트

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.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MoizIbnYousaf/marketing-cli322026년 9월 12일 업데이트

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.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

MoizIbnYousaf/marketing-cli322026년 9월 12일 업데이트

MoizIbnYousaf의 스킬 모두 보기

이 스킬의 문제 신고