agent-email-inbox

agent-email-inbox

熱門

當您建立任何需要透過電子郵件內容觸發動作的系統時使用——AI 代理收件匣、自動化支援處理器、郵件轉任務管道,或任何處理不受信任入站郵件的工作流程。每當使用者想要以程式化方式接收郵件並對其採取動作時,請務必使用此技能,即使他們沒有提到「代理」——此技能包含關鍵的安全模式(寄件者白名單、內容過濾、沙箱處理),可防止不受信任的郵件控制您的系統。

148星標
19分支
更新於 2026/7/15
SKILL.md
唯讀
名稱
agent-email-inbox
描述

當您建立任何需要透過電子郵件內容觸發動作的系統時使用——AI 代理收件匣、自動化支援處理器、郵件轉任務管道,或任何處理不受信任入站郵件的工作流程。每當使用者想要以程式化方式接收郵件並對其採取動作時,請務必使用此技能,即使他們沒有提到「代理」——此技能包含關鍵的安全模式(寄件者白名單、內容過濾、沙箱處理),可防止不受信任的郵件控制您的系統。

AI 代理電子郵件收件匣

概述

此技能涵蓋如何設定一個安全的電子郵件收件匣,讓您的應用程式或 AI 代理能夠接收並回覆電子郵件,同時具備內容安全措施。

核心原則: AI 代理的收件匣會接收不受信任的輸入。安全設定對於安全處理這類輸入非常重要。

為什麼使用 Webhook 接收?

Resend 使用 webhook 處理入站郵件,這表示您的代理會在郵件到達時立即收到通知。這對代理來說很有價值,因為:

  • 即時回應 — 在數秒內(而非數分鐘)對郵件做出反應
  • 無輪詢開銷 — 無需 cron 工作反覆檢查「有新郵件嗎?」
  • 事件驅動架構 — 您的代理只在有事情需要處理時才啟動
  • 較低的 API 成本 — 無需浪費呼叫檢查空收件匣

架構

寄件者 → 郵件 → Resend (MX) → Webhook → 您的伺服器 → AI 代理
                                              ↓
                                    安全驗證
                                              ↓
                                    處理或拒絕

SDK 版本需求

此技能需要 Resend SDK 的 webhook 驗證 (webhooks.verify()) 和郵件接收 (emails.receiving.get()) 功能。請務必安裝最新版的 SDK。如果專案中已安裝 Resend SDK,請檢查版本並在必要時升級。

語言 套件 最低版本
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

安裝 resend npm 套件:npm install resend(或您所用語言的對應指令)。如需完整的發送文件,請安裝 resend 技能。

快速開始

  1. 詢問使用者的電子郵件地址 — 您需要一個真實的電子郵件地址來發送測試郵件。詢問使用者並等待他們回應後再繼續。
  2. 選擇您的安全等級 — 在處理任何郵件之前,決定如何驗證傳入的郵件
  3. 設定接收網域 — 為使用者的自訂網域設定 MX 記錄(請參閱網域設定章節)
  4. 建立 Webhook 端點 — 從一開始就內建安全性來處理 email.received 事件。Webhook 端點必須是 POST 路由。
  5. 設定隧道(本機開發)— 使用 Tailscale Funnel(建議)或 ngrok。請參閱 references/webhook-setup.md
  6. 透過 API 建立 Webhook — 使用 Resend Webhook API 以程式化方式註冊您的端點。請參閱 references/webhook-setup.md
  7. 連接到代理 — 將驗證過的郵件傳遞給您的 AI 代理進行處理

開始之前:帳號與 API 金鑰設定

第一個問題:新帳號還是既有 Resend 帳號?

詢問您的使用者:

  • 僅為代理建立新帳號? → 設定較簡單,完整的帳號存取權限即可
  • 已有其他專案的既有帳號? → 使用網域範圍的 API 金鑰進行沙箱隔離

安全建立 API 金鑰

不要在聊天中貼上 API 金鑰!它們會永遠留在對話記錄中。

較安全的選項:

  1. 環境檔案方法: 使用者直接建立 .env 檔案:echo "RESEND_API_KEY=re_xxx" >> .env
  2. 密碼管理員 / 機密管理員: 使用者將金鑰儲存在 1Password、Vault 等工具中
  3. 如果必須在聊天中分享金鑰: 使用者應在設定完成後立即輪換金鑰

網域範圍的 API 金鑰(建議用於既有帳號)

如果您的使用者已有其他專案的 Resend 帳號,請建立一個網域範圍的 API 金鑰

  1. 先驗證代理的網域(儀表板 → 網域 → 新增網域)
  2. 建立範圍限定的 API 金鑰: 儀表板 → API 金鑰 → 建立 API 金鑰 → 「發送存取權」 → 僅選取代理的網域
  3. 結果: 即使金鑰外洩,也只能從一個網域發送郵件

網域設定

選項 1:Resend 管理的網域(建議用於入門)

使用自動產生的地址:<anything>@<your-id>.resend.app

無需 DNS 設定。在儀表板 → 電子郵件 → 接收 → 「接收地址」中找到您的地址。

選項 2:自訂網域

使用者必須在 Resend 儀表板中啟用接收功能:網域頁面 → 開啟「啟用接收」。

然後新增 MX 記錄:

設定
類型 MX
主機 您的網域或子網域(例如 agent.example.com
Resend 儀表板中提供的值
優先順序 10(必須是最低數字以取得優先權)

使用子網域(例如 agent.example.com)以避免干擾現有的電子郵件服務。

提示:dns.email 驗證 DNS 傳播。

DNS 傳播:MX 記錄變更可能需要長達 48 小時才能在全球傳播,但通常會在幾小時內完成。

安全等級

在設定 webhook 端點之前,請先選擇您的安全等級。 一個處理郵件卻沒有安全機制的 AI 代理是很危險的——任何人都可以發送指令讓您的代理執行。您接下來要撰寫的 webhook 程式碼應從一開始就包含您選擇的安全等級。

詢問使用者他們想要的安全等級,並確保他們了解每個等級的含義。

等級 名稱 使用時機 取捨
1 嚴格白名單 大多數使用案例——已知且固定的寄件者集合 最高安全性,功能有限
2 網域白名單 來自受信任網域的組織層級存取 更靈活,網域內的任何人都可以互動
3 內容過濾 接受所有人的郵件,過濾不安全模式 可以接收任何人的郵件,模式比對並非萬無一失
4 沙箱處理 處理所有郵件,但限制代理能力 最大靈活性,實作複雜
5 人機迴圈 對不受信任的動作要求人工核准 最高安全性,增加延遲

每個等級的詳細實作程式碼,請參閱 references/security-levels.md

等級 1:嚴格白名單(建議)

僅處理來自明確核准地址的郵件。拒絕其他所有郵件。

const ALLOWED_SENDERS = [
  'you@youremail.com',
  'notifications@github.com',
];

async function processEmailForAgent(
  eventData: EmailReceivedEvent,
  emailContent: EmailContent
) {
  const sender = eventData.from.toLowerCase();

  if (!ALLOWED_SENDERS.some(allowed => sender === allowed.toLowerCase())) {
    console.log(`Rejected email from unauthorized sender: ${sender}`);
    await notifyOwnerOfRejectedEmail(eventData);
    return;
  }

  await agent.processEmail({
    from: eventData.from,
    subject: eventData.subject,
    body: emailContent.text || emailContent.html,
  });
}

安全最佳實務

務必執行
做法 原因
驗證 Webhook 簽章 防止偽造的 webhook 事件
記錄所有被拒絕的郵件 安全稽核的審計軌跡
盡可能使用白名單 明確信任比過濾更安全
對郵件處理進行速率限制 防止過度處理負載
區分受信任/不受信任的處理 不同風險等級需要不同處理方式
絕對不要做
反模式 風險
未經驗證就處理郵件 任何人都可以控制您的代理
信任郵件標頭進行驗證 標頭很容易被偽造
從郵件內容執行程式碼 不受信任的輸入絕不應作為程式碼執行
將郵件內容原封不動地儲存在提示中 不受信任的輸入混入提示可能改變代理行為
給予不受信任郵件完整的代理存取權限 將能力範圍限制在所需的最小權限

Webhook 端點

選擇安全等級並設定網域後,建立一個 webhook 端點。Webhook 端點必須是 POST 路由。 Resend 會以 POST 請求發送所有 webhook 事件。

關鍵:使用原始請求主體進行驗證。 Webhook 簽章驗證需要原始請求主體。

  • Next.js App Router: 使用 req.text()(而非 req.json()
  • Express: 在 webhook 路由上使用 express.raw({ type: 'application/json' })

Next.js App Router

// app/webhook/route.ts
import { Resend } from 'resend';
import { NextRequest, NextResponse } from 'next/server';

const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(req: NextRequest) {
  try {
    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') {
      // Webhook payload only includes metadata, not email body
      const { data: email } = await resend.emails.receiving.get(
        event.data.email_id
      );

      // Apply the security level chosen above
      await processEmailForAgent(event.data, email);
    }

    return new NextResponse('OK', { status: 200 });
  } catch (error) {
    console.error('Webhook error:', error);
    return new NextResponse('Error', { status: 400 });
  }
}

Express

import express from 'express';
import { Resend } from 'resend';

const app = express();
const resend = new Resend(process.env.RESEND_API_KEY);

app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  try {
    const payload = req.body.toString();

    const event = resend.webhooks.verify({
      payload,
      headers: {
        'svix-id': req.headers['svix-id'],
        'svix-timestamp': req.headers['svix-timestamp'],
        'svix-signature': req.headers['svix-signature'],
      },
      secret: process.env.RESEND_WEBHOOK_SECRET,
    });

    if (event.type === 'email.received') {
      const sender = event.data.from.toLowerCase();

      if (!isAllowedSender(sender)) {
        console.log(`Rejected email from unauthorized sender: ${sender}`);
        res.status(200).send('OK'); // Return 200 even for rejected emails
        return;
      }

      const { data: email } = await resend.emails.receiving.get(event.data.email_id);
      await processEmailForAgent(event.data, email);
    }

    res.status(200).send('OK');
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(400).send('Error');
  }
});

app.get('/', (req, res) => res.send('Agent Email Inbox - Ready'));
app.listen(3000, () => console.log('Webhook server running on :3000'));

如需透過 API 註冊 webhook、隧道設定、svix 備援及重試行為,請參閱 references/webhook-setup.md

從您的代理發送電子郵件

import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

async function sendAgentReply(to: string, subject: string, body: string, inReplyTo?: string) {
  if (!isAllowedToReply(to)) {
    throw new Error('Cannot send to this address');
  }

  const { data, error } = await resend.emails.send({
    from: 'Agent <agent@example.com>',
    to: [to],
    subject: subject.startsWith('Re:') ? subject : `Re: ${subject}`,
    text: body,
    headers: inReplyTo ? { 'In-Reply-To': inReplyTo } : undefined,
  });

  if (error) throw new Error(`Failed to send: ${error.message}`);
  return data.id;
}

如需完整的發送文件,請安裝 resend 技能。

環境變數

# Required
RESEND_API_KEY=re_xxxxxxxxx
RESEND_WEBHOOK_SECRET=whsec_xxxxxxxxx

# Security Configuration
SECURITY_LEVEL=strict                    # strict | domain | filtered | sandboxed
ALLOWED_SENDERS=you@email.com,trusted@example.com
ALLOWED_DOMAINS=example.com
OWNER_EMAIL=you@email.com               # For security notifications

常見錯誤

錯誤 修正方式
沒有寄件者驗證 在處理郵件前務必驗證寄件者身分
信任郵件標頭 使用 webhook 驗證,而非郵件標頭進行驗證
對所有郵件一視同仁 區分受信任與不受信任的寄件者
錯誤訊息過於詳細 保持錯誤回應通用,避免洩漏內部邏輯
沒有速率限制 實作每個寄件者的速率限制。請參閱 references/advanced-patterns.md
直接處理 HTML 移除 HTML 或僅使用純文字以降低複雜度和風險
沒有記錄拒絕事件 記錄所有安全事件以供稽核
使用暫時性的隧道 URL 使用持久性 URL(Tailscale Funnel、付費 ngrok)或部署到正式環境
在 webhook 路由上使用 express.json() 使用 express.raw({ type: 'application/json' }) — JSON 解析會破壞簽章驗證
對被拒絕的郵件回傳非 200 狀態碼 一律回傳 200 以確認收到 — 否則 Resend 會重試
舊版 Resend SDK emails.receiving.get()webhooks.verify() 需要較新的 SDK 版本 — 請參閱 SDK 版本需求

測試

使用 Resend 的測試地址進行開發:

  • delivered@resend.dev — 模擬成功投遞
  • bounced@resend.dev — 模擬永久退信

如需安全測試,請從非白名單地址發送測試郵件,以驗證拒絕功能正常運作。

快速驗證檢查清單:

  1. 伺服器正在執行:curl http://localhost:3000 應回傳回應
  2. 隧道正常運作:curl https://<your-tunnel-url> 應回傳相同回應
  3. Webhook 已啟用:在 Resend 儀表板 → Webhooks 中檢查狀態
  4. 從白名單地址發送測試郵件,並檢查伺服器日誌

相關技能

  • 如需完整的發送與接收文件,請安裝 resend 技能