Receber por email
← Voltar para o catálogo

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

25estrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add MoizIbnYousaf/marketing-cli

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/resend-inbound/svg)](https://www.skillteca.com.br/skills/resend-inbound)
#ai

Skills relacionadas

Ver todas de Pesquisa e Web →

Alerta por categoria

Receba novas skills de Pesquisa e Web toda segunda

1 email curto, só com as skills novas de Pesquisa e Web. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

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.

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

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

Anti-Patterns

| 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

Compartilhar X LinkedIn