[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71P0WCM9NDKNQESKNFVC5RB":3,"skill-same:01M71P0WCM9NDKNQESKNFVC5RB":112},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":90,"commitSha":91,"skillDirectory":92,"forksCount":93,"repositoryHtmlUrl":94,"repositoryHomepageUrl":6,"ownerHtmlUrl":95,"ownerBlogUrl":96,"ownerDescription":97,"updatedAt":98,"install":99,"resources":111},"01M71P0WCM9NDKNQESKNFVC5RB","agent-email-inbox",null,"Use when building any system where email content triggers actions — AI agent inboxes, automated support handlers, email-to-task pipelines, or any workflow processing untrusted inbound email. Always use this skill when the user wants to receive emails and act on them programmatically, even if they don't mention \"agent\" — the skill contains critical security patterns (sender allowlists, content filtering, sandboxed processing) that prevent untrusted email from controlling your system.","iDorgham\u002FWorkspace-Factory","iDorgham","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F136521493?v=4","01M6CNGBDAHE4ARW3YJEF4X2FD","Dorgham","01M71E8H3CXZ7DJX59MTM9JEB2",1,"MIT","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_resend_agent_email_inbox\u002Fskill.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000005","邮件与沟通","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"email","agent email inbox","inbox","notifications","api","you","workflow","when","critical","false","ai agent","Content Filtering","secret","domains","actions","advanced","get","webhook","processing","resend","2026-11-10 10:56:23",false,"---\nname: agent-email-inbox\ndescription: Use when building any system where email content triggers actions — AI agent inboxes, automated support handlers, email-to-task pipelines, or any workflow processing untrusted inbound email. Always use this skill when the user wants to receive emails and act on them programmatically, even if they don't mention \"agent\" — the skill contains critical security patterns (sender allowlists, content filtering, sandboxed processing) that prevent untrusted email from controlling your system.\nlicense: MIT\nmetadata:\n    author: resend\n    version: \"3.0.2\"\n    homepage: https:\u002F\u002Fresend.com\u002Fagent-skills\n    source: https:\u002F\u002Fgithub.com\u002Fresend\u002Fresend-skills\n    openclaw:\n        primaryEnv: RESEND_API_KEY\n        requires:\n            env:\n                - RESEND_API_KEY\n        envVars:\n            - name: RESEND_API_KEY\n              required: true\n              description: Resend API key for sending and receiving emails\n            - name: RESEND_WEBHOOK_SECRET\n              required: false\n              description: Webhook signing secret for verifying inbound email event payloads\n            - name: SECURITY_LEVEL\n              required: false\n              description: Security level for inbound email processing (strict, moderate, permissive)\n            - name: ALLOWED_SENDERS\n              required: false\n              description: Comma-separated list of allowed sender email addresses\n            - name: ALLOWED_DOMAINS\n              required: false\n              description: Comma-separated list of allowed sender domains\n            - name: OWNER_EMAIL\n              required: false\n              description: Owner email address for forwarding or notifications\n        links:\n            repository: https:\u002F\u002Fgithub.com\u002Fresend\u002Fresend-skills\n            documentation: https:\u002F\u002Fresend.com\u002Fdocs\u002Fagent-email-inbox-skill\ninputs:\n    - name: RESEND_API_KEY\n      description: Resend API key for sending and receiving emails. Get yours at https:\u002F\u002Fresend.com\u002Fapi-keys\n      required: true\n    - name: RESEND_WEBHOOK_SECRET\n      description: Webhook signing secret for verifying inbound email event payloads. Returned as `signing_secret` in the response when you create a webhook via the API.\n      required: true\nreferences:\n    - security_levels.md\n    - webhook_setup.md\n    - advanced_patterns.md\n---\n\n# AI Agent Email Inbox\n\n## Overview\n\nThis skill covers setting up a secure email inbox that allows your application or AI agent to receive and respond to emails, with content safety measures in place.\n\n**Core principle:** An AI agent's inbox receives untrusted input. Security configuration is important to handle this safely.\n\n### Why Webhook-Based Receiving?\n\nResend uses webhooks for inbound email, meaning your agent is notified **instantly** when an email arrives. This is valuable for agents because:\n\n- **Real-time responsiveness** — React to emails within seconds, not minutes\n- **No polling overhead** — No cron jobs checking \"any new mail?\" repeatedly\n- **Event-driven architecture** — Your agent only wakes up when there's actually something to process\n- **Lower API costs** — No wasted calls checking empty inboxes\n\n## Architecture\n\n```\nSender → Email → Resend (MX) → Webhook → Your Server → AI Agent\n                                              ↓\n                                    Security Validation\n                                              ↓\n                                    Process or Reject\n```\n\n## SDK Version Requirements\n\nThis 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.\n\n| Language | Package | Min Version |\n|----------|---------|-------------|\n| Node.js | `resend` | >= 6.9.2 |\n| Python | `resend` | >= 2.21.0 |\n| Go | `resend-go\u002Fv3` | >= 3.1.0 |\n| Ruby | `resend` | >= 1.0.0 |\n| PHP | `resend\u002Fresend-php` | >= 1.1.0 |\n| Rust | `resend-rs` | >= 0.20.0 |\n| Java | `resend-java` | >= 4.11.0 |\n| .NET | `Resend` | >= 0.2.1 |\n\nInstall the `resend` npm package: `npm install resend` (or the equivalent for your language). For full sending docs, install the `resend` skill.\n\n## Quick Start\n\n1. **Ask the user for their email address** — You need a real email address to send test emails to. Ask the user and wait for their response before proceeding.\n2. **Choose your security level** — Decide how to validate incoming emails *before* any are processed\n3. **Set up receiving domain** — Configure MX records for the user's custom domain (see Domain Setup section)\n4. **Create webhook endpoint** — Handle `email.received` events with security built in from the start. **The webhook endpoint MUST be a POST route.**\n5. **Set up tunneling** (local dev) — Use Tailscale Funnel (recommended) or ngrok. See references\u002Fwebhook_setup.md (`references\u002Fwebhook_setup.md`)\n6. **Create webhook via API** — Use the Resend Webhook API to register your endpoint programmatically. See references\u002Fwebhook_setup.md (`references\u002Fwebhook_setup.md`)\n7. **Connect to agent** — Pass validated emails to your AI agent for processing\n\n## Before You Start: Account & API Key Setup\n\n### First Question: New or Existing Resend Account?\n\nAsk your human:\n- **New account just for the agent?** → Simpler setup, full account access is fine\n- **Existing account with other projects?** → Use domain-scoped API keys for sandboxing\n\n### Creating API Keys Securely\n\n> Don't paste API keys in chat! They'll be in conversation history forever.\n\n**Safer options:**\n\n1. **Environment file method:** Human creates `.env` file directly: `echo \"RESEND_API_KEY=re_xxx\" >> .env`\n2. **Password manager \u002F secrets manager:** Human stores key in 1Password, Vault, etc.\n3. **If key must be shared in chat:** Human should rotate the key immediately after setup\n\n### Domain-Scoped API Keys (Recommended for Existing Accounts)\n\nIf your human has an existing Resend account with other projects, create a **domain-scoped API key**:\n\n1. **Verify the agent's domain first** (Dashboard → Domains → Add Domain)\n2. **Create a scoped API key:** Dashboard → API Keys → Create API Key → \"Sending access\" → select only the agent's domain\n3. **Result:** Even if the key leaks, it can only send from one domain\n\n## Domain Setup\n\n### Option 1: Resend-Managed Domain (Recommended for Getting Started)\n\nUse your auto-generated address: `\u003Canything>@\u003Cyour-id>.resend.app`\n\nNo DNS configuration needed. Find your address in Dashboard → Emails → Receiving → \"Receiving address\".\n\n### Option 2: Custom Domain\n\nThe user must enable receiving in the Resend dashboard: Domains page → toggle on \"Enable Receiving\".\n\nThen add an MX record:\n\n| Setting | Value |\n|---------|-------|\n| **Type** | MX |\n| **Host** | Your domain or subdomain (e.g., `agent.example.com`) |\n| **Value** | Provided in Resend dashboard |\n| **Priority** | 10 (must be lowest number to take precedence) |\n\n**Use a subdomain** (e.g., `agent.example.com`) to avoid disrupting existing email services.\n\n**Tip:** Verify DNS propagation at [dns.email](https:\u002F\u002Fdns.email).\n\n> DNS Propagation: MX record changes can take up to 48 hours to propagate globally, though often complete within a few hours.\n\n## Security Levels\n\n**Choose your security level before setting up the webhook endpoint.** An AI agent that processes emails without security is dangerous — anyone can email instructions that your agent will execute. The webhook code you write next should include your chosen security level from the start.\n\nAsk the user what level of security they want, and ensure that they understand what each level means.\n\n| Level | Name | When to Use | Trade-off |\n|-------|------|-------------|-----------|\n| **1** | Strict Allowlist | Most use cases — known, fixed set of senders | Maximum security, limited functionality |\n| **2** | Domain Allowlist | Organization-wide access from trusted domains | More flexible, anyone at domain can interact |\n| **3** | Content Filtering | Accept from anyone, filter unsafe patterns | Can receive from anyone, pattern matching not foolproof |\n| **4** | Sandboxed Processing | Process all emails with restricted agent capabilities | Maximum flexibility, complex to implement |\n| **5** | Human-in-the-Loop | Require human approval for untrusted actions | Maximum security, adds latency |\n\nFor detailed implementation code for each level, see references\u002Fsecurity_levels.md (`references\u002Fsecurity_levels.md`).\n\n### Level 1: Strict Allowlist (Recommended)\n\nOnly process emails from explicitly approved addresses. Reject everything else.\n\n```typescript\nconst ALLOWED_SENDERS = [\n  'you@youremail.com',\n  'notifications@github.com',\n];\n\nasync function processEmailForAgent(\n  eventData: EmailReceivedEvent,\n  emailContent: EmailContent\n) {\n  const sender = eventData.from.toLowerCase();\n\n  if (!ALLOWED_SENDERS.some(allowed => sender === allowed.toLowerCase())) {\n    console.log(`Rejected email from unauthorized sender: ${sender}`);\n    await notifyOwnerOfRejectedEmail(eventData);\n    return;\n  }\n\n  await agent.processEmail({\n    from: eventData.from,\n    subject: eventData.subject,\n    body: emailContent.text || emailContent.html,\n  });\n}\n```\n\n### Security Best Practices\n\n#### Always Do\n\n| Practice | Why |\n|----------|-----|\n| Verify webhook signatures | Prevents spoofed webhook events |\n| Log all rejected emails | Audit trail for security review |\n| Use allowlists where possible | Explicit trust is safer than filtering |\n| Rate limit email processing | Prevents excessive processing load |\n| Separate trusted\u002Funtrusted handling | Different risk levels need different treatment |\n\n#### Never Do\n\n| Anti-Pattern | Risk |\n|--------------|------|\n| Process emails without validation | Anyone can control your agent |\n| Trust email headers for authentication | Headers are trivially spoofed |\n| Execute code from email content | Untrusted input should never run as code |\n| Store email content in prompts verbatim | Untrusted input mixed into prompts can alter agent behavior |\n| Give untrusted emails full agent access | Scope capabilities to the minimum needed |\n\n## Webhook Endpoint\n\nAfter choosing your security level and setting up your domain, create a webhook endpoint. **The webhook endpoint MUST be a POST route.** Resend sends all webhook events as POST requests.\n\n> **Critical: Use raw body for verification.** Webhook signature verification requires the raw request body.\n> - **Next.js App Router:** Use `req.text()` (not `req.json()`)\n> - **Express:** Use `express.raw({ type: 'application\u002Fjson' })` on the webhook route\n\n### Next.js App Router\n\n```typescript\n\u002F\u002F app\u002Fwebhook\u002Froute.ts\nimport { Resend } from 'resend';\nimport { NextRequest, NextResponse } from 'next\u002Fserver';\n\nconst resend = new Resend(process.env.RESEND_API_KEY);\n\nexport async function POST(req: NextRequest) {\n  try {\n    const payload = await req.text();\n\n    const event = resend.webhooks.verify({\n      payload,\n      headers: {\n        'svix-id': req.headers.get('svix-id'),\n        'svix-timestamp': req.headers.get('svix-timestamp'),\n        'svix-signature': req.headers.get('svix-signature'),\n      },\n      secret: process.env.RESEND_WEBHOOK_SECRET,\n    });\n\n    if (event.type === 'email.received') {\n      \u002F\u002F Webhook payload only includes metadata, not email body\n      const { data: email } = await resend.emails.receiving.get(\n        event.data.email_id\n      );\n\n      \u002F\u002F Apply the security level chosen above\n      await processEmailForAgent(event.data, email);\n    }\n\n    return new NextResponse('OK', { status: 200 });\n  } catch (error) {\n    console.error('Webhook error:', error);\n    return new NextResponse('Error', { status: 400 });\n  }\n}\n```\n\n### Express\n\n```javascript\nimport express from 'express';\nimport { Resend } from 'resend';\n\nconst app = express();\nconst resend = new Resend(process.env.RESEND_API_KEY);\n\napp.post('\u002Fwebhook', express.raw({ type: 'application\u002Fjson' }), async (req, res) => {\n  try {\n    const payload = req.body.toString();\n\n    const event = resend.webhooks.verify({\n      payload,\n      headers: {\n        'svix-id': req.headers['svix-id'],\n        'svix-timestamp': req.headers['svix-timestamp'],\n        'svix-signature': req.headers['svix-signature'],\n      },\n      secret: process.env.RESEND_WEBHOOK_SECRET,\n    });\n\n    if (event.type === 'email.received') {\n      const sender = event.data.from.toLowerCase();\n\n      if (!isAllowedSender(sender)) {\n        console.log(`Rejected email from unauthorized sender: ${sender}`);\n        res.status(200).send('OK'); \u002F\u002F Return 200 even for rejected emails\n        return;\n      }\n\n      const { data: email } = await resend.emails.receiving.get(event.data.email_id);\n      await processEmailForAgent(event.data, email);\n    }\n\n    res.status(200).send('OK');\n  } catch (error) {\n    console.error('Webhook error:', error);\n    res.status(400).send('Error');\n  }\n});\n\napp.get('\u002F', (req, res) => res.send('Agent Email Inbox - Ready'));\napp.listen(3000, () => console.log('Webhook server running on :3000'));\n```\n\nFor webhook registration via API, tunneling setup, svix fallback, and retry behavior, see references\u002Fwebhook_setup.md (`references\u002Fwebhook_setup.md`).\n\n## Sending Emails from Your Agent\n\n```typescript\nimport { Resend } from 'resend';\n\nconst resend = new Resend(process.env.RESEND_API_KEY);\n\nasync function sendAgentReply(to: string, subject: string, body: string, inReplyTo?: string) {\n  if (!isAllowedToReply(to)) {\n    throw new Error('Cannot send to this address');\n  }\n\n  const { data, error } = await resend.emails.send({\n    from: 'Agent \u003Cagent@example.com>',\n    to: [to],\n    subject: subject.startsWith('Re:') ? subject : `Re: ${subject}`,\n    text: body,\n    headers: inReplyTo ? { 'In-Reply-To': inReplyTo } : undefined,\n  });\n\n  if (error) throw new Error(`Failed to send: ${error.message}`);\n  return data.id;\n}\n```\n\nFor full sending docs, install the `resend` skill.\n\n## Environment Variables\n\n```bash\n# Required\nRESEND_API_KEY=re_xxxxxxxxx\nRESEND_WEBHOOK_SECRET=whsec_xxxxxxxxx\n\n# Security Configuration\nSECURITY_LEVEL=strict                    # strict | domain | filtered | sandboxed\nALLOWED_SENDERS=you@email.com,trusted@example.com\nALLOWED_DOMAINS=example.com\nOWNER_EMAIL=you@email.com               # For security notifications\n```\n\n## Common Mistakes\n\n| Mistake | Fix |\n|---------|-----|\n| No sender verification | Always validate who sent the email before processing |\n| Trusting email headers | Use webhook verification, not email headers for auth |\n| Same treatment for all emails | Differentiate trusted vs untrusted senders |\n| Verbose error messages | Keep error responses generic to avoid leaking internal logic |\n| No rate limiting | Implement per-sender rate limits. See references\u002Fadvanced_patterns.md (`references\u002Fadvanced_patterns.md`) |\n| Processing HTML directly | Strip HTML or use text-only to reduce complexity and risk |\n| No logging of rejections | Log all security events for audit |\n| Using ephemeral tunnel URLs | Use persistent URLs (Tailscale Funnel, paid ngrok) or deploy to production |\n| Using `express.json()` on webhook route | Use `express.raw({ type: 'application\u002Fjson' })` — JSON parsing breaks signature verification |\n| Returning non-200 for rejected emails | Always return 200 to acknowledge receipt — otherwise Resend retries |\n| Old Resend SDK version | `emails.receiving.get()` and `webhooks.verify()` require recent SDK versions — see SDK Version Requirements |\n\n## Testing\n\nUse Resend's test addresses for development:\n- `delivered@resend.dev` — Simulates successful delivery\n- `bounced@resend.dev` — Simulates hard bounce\n\nFor security testing, send test emails from non-allowlisted addresses to verify rejection works correctly.\n\n**Quick verification checklist:**\n1. Server is running: `curl http:\u002F\u002Flocalhost:3000` should return a response\n2. Tunnel is working: `curl https:\u002F\u002F\u003Cyour-tunnel-url>` should return the same response\n3. Webhook is active: Check status in Resend dashboard → Webhooks\n4. Send a test email from an allowlisted address and check server logs\n\n## Related Skills\n\n- For full sending and receiving docs, install the `resend` skill\n","# AI Agent Email Inbox\n\n## Overview\n\nThis skill covers setting up a secure email inbox that allows your application or AI agent to receive and respond to emails, with content safety measures in place.\n\n**Core principle:** An AI agent's inbox receives untrusted input. Security configuration is important to handle this safely.\n\n### Why Webhook-Based Receiving?\n\nResend uses webhooks for inbound email, meaning your agent is notified **instantly** when an email arrives. This is valuable for agents because:\n\n- **Real-time responsiveness** — React to emails within seconds, not minutes\n- **No polling overhead** — No cron jobs checking \"any new mail?\" repeatedly\n- **Event-driven architecture** — Your agent only wakes up when there's actually something to process\n- **Lower API costs** — No wasted calls checking empty inboxes\n\n## Architecture\n\n```\nSender → Email → Resend (MX) → Webhook → Your Server → AI Agent\n                                              ↓\n                                    Security Validation\n                                              ↓\n                                    Process or Reject\n```\n\n## SDK Version Requirements\n\nThis 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.\n\n| Language | Package | Min Version |\n|----------|---------|-------------|\n| Node.js | `resend` | >= 6.9.2 |\n| Python | `resend` | >= 2.21.0 |\n| Go | `resend-go\u002Fv3` | >= 3.1.0 |\n| Ruby | `resend` | >= 1.0.0 |\n| PHP | `resend\u002Fresend-php` | >= 1.1.0 |\n| Rust | `resend-rs` | >= 0.20.0 |\n| Java | `resend-java` | >= 4.11.0 |\n| .NET | `Resend` | >= 0.2.1 |\n\nInstall the `resend` npm package: `npm install resend` (or the equivalent for your language). For full sending docs, install the `resend` skill.\n\n## Quick Start\n\n1. **Ask the user for their email address** — You need a real email address to send test emails to. Ask the user and wait for their response before proceeding.\n2. **Choose your security level** — Decide how to validate incoming emails *before* any are processed\n3. **Set up receiving domain** — Configure MX records for the user's custom domain (see Domain Setup section)\n4. **Create webhook endpoint** — Handle `email.received` events with security built in from the start. **The webhook endpoint MUST be a POST route.**\n5. **Set up tunneling** (local dev) — Use Tailscale Funnel (recommended) or ngrok. See references\u002Fwebhook_setup.md (`references\u002Fwebhook_setup.md`)\n6. **Create webhook via API** — Use the Resend Webhook API to register your endpoint programmatically. See references\u002Fwebhook_setup.md (`references\u002Fwebhook_setup.md`)\n7. **Connect to agent** — Pass validated emails to your AI agent for processing\n\n## Before You Start: Account & API Key Setup\n\n### First Question: New or Existing Resend Account?\n\nAsk your human:\n- **New account just for the agent?** → Simpler setup, full account access is fine\n- **Existing account with other projects?** → Use domain-scoped API keys for sandboxing\n\n### Creating API Keys Securely\n\n> Don't paste API keys in chat! They'll be in conversation history forever.\n\n**Safer options:**\n\n1. **Environment file method:** Human creates `.env` file directly: `echo \"RESEND_API_KEY=re_xxx\" >> .env`\n2. **Password manager \u002F secrets manager:** Human stores key in 1Password, Vault, etc.\n3. **If key must be shared in chat:** Human should rotate the key immediately after setup\n\n### Domain-Scoped API Keys (Recommended for Existing Accounts)\n\nIf your human has an existing Resend account with other projects, create a **domain-scoped API key**:\n\n1. **Verify the agent's domain first** (Dashboard → Domains → Add Domain)\n2. **Create a scoped API key:** Dashboard → API Keys → Create API Key → \"Sending access\" → select only the agent's domain\n3. **Result:** Even if the key leaks, it can only send from one domain\n\n## Domain Setup\n\n### Option 1: Resend-Managed Domain (Recommended for Getting Started)\n\nUse your auto-generated address: `\u003Canything>@\u003Cyour-id>.resend.app`\n\nNo DNS configuration needed. Find your address in Dashboard → Emails → Receiving → \"Receiving address\".\n\n### Option 2: Custom Domain\n\nThe user must enable receiving in the Resend dashboard: Domains page → toggle on \"Enable Receiving\".\n\nThen add an MX record:\n\n| Setting | Value |\n|---------|-------|\n| **Type** | MX |\n| **Host** | Your domain or subdomain (e.g., `agent.example.com`) |\n| **Value** | Provided in Resend dashboard |\n| **Priority** | 10 (must be lowest number to take precedence) |\n\n**Use a subdomain** (e.g., `agent.example.com`) to avoid disrupting existing email services.\n\n**Tip:** Verify DNS propagation at [dns.email](https:\u002F\u002Fdns.email).\n\n> DNS Propagation: MX record changes can take up to 48 hours to propagate globally, though often complete within a few hours.\n\n## Security Levels\n\n**Choose your security level before setting up the webhook endpoint.** An AI agent that processes emails without security is dangerous — anyone can email instructions that your agent will execute. The webhook code you write next should include your chosen security level from the start.\n\nAsk the user what level of security they want, and ensure that they understand what each level means.\n\n| Level | Name | When to Use | Trade-off |\n|-------|------|-------------|-----------|\n| **1** | Strict Allowlist | Most use cases — known, fixed set of senders | Maximum security, limited functionality |\n| **2** | Domain Allowlist | Organization-wide access from trusted domains | More flexible, anyone at domain can interact |\n| **3** | Content Filtering | Accept from anyone, filter unsafe patterns | Can receive from anyone, pattern matching not foolproof |\n| **4** | Sandboxed Processing | Process all emails with restricted agent capabilities | Maximum flexibility, complex to implement |\n| **5** | Human-in-the-Loop | Require human approval for untrusted actions | Maximum security, adds latency |\n\nFor detailed implementation code for each level, see references\u002Fsecurity_levels.md (`references\u002Fsecurity_levels.md`).\n\n### Level 1: Strict Allowlist (Recommended)\n\nOnly process emails from explicitly approved addresses. Reject everything else.\n\n```typescript\nconst ALLOWED_SENDERS = [\n  'you@youremail.com',\n  'notifications@github.com',\n];\n\nasync function processEmailForAgent(\n  eventData: EmailReceivedEvent,\n  emailContent: EmailContent\n) {\n  const sender = eventData.from.toLowerCase();\n\n  if (!ALLOWED_SENDERS.some(allowed => sender === allowed.toLowerCase())) {\n    console.log(`Rejected email from unauthorized sender: ${sender}`);\n    await notifyOwnerOfRejectedEmail(eventData);\n    return;\n  }\n\n  await agent.processEmail({\n    from: eventData.from,\n    subject: eventData.subject,\n    body: emailContent.text || emailContent.html,\n  });\n}\n```\n\n### Security Best Practices\n\n#### Always Do\n\n| Practice | Why |\n|----------|-----|\n| Verify webhook signatures | Prevents spoofed webhook events |\n| Log all rejected emails | Audit trail for security review |\n| Use allowlists where possible | Explicit trust is safer than filtering |\n| Rate limit email processing | Prevents excessive processing load |\n| Separate trusted\u002Funtrusted handling | Different risk levels need different treatment |\n\n#### Never Do\n\n| Anti-Pattern | Risk |\n|--------------|------|\n| Process emails without validation | Anyone can control your agent |\n| Trust email headers for authentication | Headers are trivially spoofed |\n| Execute code from email content | Untrusted input should never run as code |\n| Store email content in prompts verbatim | Untrusted input mixed into prompts can alter agent behavior |\n| Give untrusted emails full agent access | Scope capabilities to the minimum needed |\n\n## Webhook Endpoint\n\nAfter choosing your security level and setting up your domain, create a webhook endpoint. **The webhook endpoint MUST be a POST route.** Resend sends all webhook events as POST requests.\n\n> **Critical: Use raw body for verification.** Webhook signature verification requires the raw request body.\n> - **Next.js App Router:** Use `req.text()` (not `req.json()`)\n> - **Express:** Use `express.raw({ type: 'application\u002Fjson' })` on the webhook route\n\n### Next.js App Router\n\n```typescript\n\u002F\u002F app\u002Fwebhook\u002Froute.ts\nimport { Resend } from 'resend';\nimport { NextRequest, NextResponse } from 'next\u002Fserver';\n\nconst resend = new Resend(process.env.RESEND_API_KEY);\n\nexport async function POST(req: NextRequest) {\n  try {\n    const payload = await req.text();\n\n    const event = resend.webhooks.verify({\n      payload,\n      headers: {\n        'svix-id': req.headers.get('svix-id'),\n        'svix-timestamp': req.headers.get('svix-timestamp'),\n        'svix-signature': req.headers.get('svix-signature'),\n      },\n      secret: process.env.RESEND_WEBHOOK_SECRET,\n    });\n\n    if (event.type === 'email.received') {\n      \u002F\u002F Webhook payload only includes metadata, not email body\n      const { data: email } = await resend.emails.receiving.get(\n        event.data.email_id\n      );\n\n      \u002F\u002F Apply the security level chosen above\n      await processEmailForAgent(event.data, email);\n    }\n\n    return new NextResponse('OK', { status: 200 });\n  } catch (error) {\n    console.error('Webhook error:', error);\n    return new NextResponse('Error', { status: 400 });\n  }\n}\n```\n\n### Express\n\n```javascript\nimport express from 'express';\nimport { Resend } from 'resend';\n\nconst app = express();\nconst resend = new Resend(process.env.RESEND_API_KEY);\n\napp.post('\u002Fwebhook', express.raw({ type: 'application\u002Fjson' }), async (req, res) => {\n  try {\n    const payload = req.body.toString();\n\n    const event = resend.webhooks.verify({\n      payload,\n      headers: {\n        'svix-id': req.headers['svix-id'],\n        'svix-timestamp': req.headers['svix-timestamp'],\n        'svix-signature': req.headers['svix-signature'],\n      },\n      secret: process.env.RESEND_WEBHOOK_SECRET,\n    });\n\n    if (event.type === 'email.received') {\n      const sender = event.data.from.toLowerCase();\n\n      if (!isAllowedSender(sender)) {\n        console.log(`Rejected email from unauthorized sender: ${sender}`);\n        res.status(200).send('OK'); \u002F\u002F Return 200 even for rejected emails\n        return;\n      }\n\n      const { data: email } = await resend.emails.receiving.get(event.data.email_id);\n      await processEmailForAgent(event.data, email);\n    }\n\n    res.status(200).send('OK');\n  } catch (error) {\n    console.error('Webhook error:', error);\n    res.status(400).send('Error');\n  }\n});\n\napp.get('\u002F', (req, res) => res.send('Agent Email Inbox - Ready'));\napp.listen(3000, () => console.log('Webhook server running on :3000'));\n```\n\nFor webhook registration via API, tunneling setup, svix fallback, and retry behavior, see references\u002Fwebhook_setup.md (`references\u002Fwebhook_setup.md`).\n\n## Sending Emails from Your Agent\n\n```typescript\nimport { Resend } from 'resend';\n\nconst resend = new Resend(process.env.RESEND_API_KEY);\n\nasync function sendAgentReply(to: string, subject: string, body: string, inReplyTo?: string) {\n  if (!isAllowedToReply(to)) {\n    throw new Error('Cannot send to this address');\n  }\n\n  const { data, error } = await resend.emails.send({\n    from: 'Agent \u003Cagent@example.com>',\n    to: [to],\n    subject: subject.startsWith('Re:') ? subject : `Re: ${subject}`,\n    text: body,\n    headers: inReplyTo ? { 'In-Reply-To': inReplyTo } : undefined,\n  });\n\n  if (error) throw new Error(`Failed to send: ${error.message}`);\n  return data.id;\n}\n```\n\nFor full sending docs, install the `resend` skill.\n\n## Environment Variables\n\n```bash\n# Required\nRESEND_API_KEY=re_xxxxxxxxx\nRESEND_WEBHOOK_SECRET=whsec_xxxxxxxxx\n\n# Security Configuration\nSECURITY_LEVEL=strict                    # strict | domain | filtered | sandboxed\nALLOWED_SENDERS=you@email.com,trusted@example.com\nALLOWED_DOMAINS=example.com\nOWNER_EMAIL=you@email.com               # For security notifications\n```\n\n## Common Mistakes\n\n| Mistake | Fix |\n|---------|-----|\n| No sender verification | Always validate who sent the email before processing |\n| Trusting email headers | Use webhook verification, not email headers for auth |\n| Same treatment for all emails | Differentiate trusted vs untrusted senders |\n| Verbose error messages | Keep error responses generic to avoid leaking internal logic |\n| No rate limiting | Implement per-sender rate limits. See references\u002Fadvanced_patterns.md (`references\u002Fadvanced_patterns.md`) |\n| Processing HTML directly | Strip HTML or use text-only to reduce complexity and risk |\n| No logging of rejections | Log all security events for audit |\n| Using ephemeral tunnel URLs | Use persistent URLs (Tailscale Funnel, paid ngrok) or deploy to production |\n| Using `express.json()` on webhook route | Use `express.raw({ type: 'application\u002Fjson' })` — JSON parsing breaks signature verification |\n| Returning non-200 for rejected emails | Always return 200 to acknowledge receipt — otherwise Resend retries |\n| Old Resend SDK version | `emails.receiving.get()` and `webhooks.verify()` require recent SDK versions — see SDK Version Requirements |\n\n## Testing\n\nUse Resend's test addresses for development:\n- `delivered@resend.dev` — Simulates successful delivery\n- `bounced@resend.dev` — Simulates hard bounce\n\nFor security testing, send test emails from non-allowlisted addresses to verify rejection works correctly.\n\n**Quick verification checklist:**\n1. Server is running: `curl http:\u002F\u002Flocalhost:3000` should return a response\n2. Tunnel is working: `curl https:\u002F\u002F\u003Cyour-tunnel-url>` should return the same response\n3. Webhook is active: Check status in Resend dashboard → Webhooks\n4. Send a test email from an allowlisted address and check server logs\n\n## Related Skills\n\n- For full sending and receiving docs, install the `resend` skill\n",{"name":5,"inputs":52,"license":15,"metadata":60,"references":86,"description":7},[53,57],{"name":54,"required":55,"description":56},"RESEND_API_KEY",true,"Resend API key for sending and receiving emails. Get yours at https:\u002F\u002Fresend.com\u002Fapi-keys",{"name":58,"required":55,"description":59},"RESEND_WEBHOOK_SECRET","Webhook signing secret for verifying inbound email event payloads. Returned as `signing_secret` in the response when you create a webhook via the API.",{"author":46,"source":61,"version":62,"homepage":63,"openclaw":64},"https:\u002F\u002Fgithub.com\u002Fresend\u002Fresend-skills","3.0.2","https:\u002F\u002Fresend.com\u002Fagent-skills",{"links":65,"envVars":67,"requires":84,"primaryEnv":54},{"repository":61,"documentation":66},"https:\u002F\u002Fresend.com\u002Fdocs\u002Fagent-email-inbox-skill",[68,70,72,75,78,81],{"name":54,"required":55,"description":69},"Resend API key for sending and receiving emails",{"name":58,"required":48,"description":71},"Webhook signing secret for verifying inbound email event payloads",{"name":73,"required":48,"description":74},"SECURITY_LEVEL","Security level for inbound email processing (strict, moderate, permissive)",{"name":76,"required":48,"description":77},"ALLOWED_SENDERS","Comma-separated list of allowed sender email addresses",{"name":79,"required":48,"description":80},"ALLOWED_DOMAINS","Comma-separated list of allowed sender domains",{"name":82,"required":48,"description":83},"OWNER_EMAIL","Owner email address for forwarding or notifications",{"env":85},[54],[87,88,89],"security_levels.md","webhook_setup.md","advanced_patterns.md","master","c2e410e11df4d80d885e0ecf0583ef3c926df736","factory\u002Flibrary\u002Fskills\u002Fofficial_resend_agent_email_inbox",0,"https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory","https:\u002F\u002Fgithub.com\u002FiDorgham","Dorgham.me","hey I'm Dorgham \r\nI code, I create, and AI codes back. \r\nMaybe we can give AI the chance!! now we begin. 🚀🤖\r\n... . . . git commit -m \"Hello, Future\" ","2026-05-04 18:39:09",{"source":8,"skillName":5,"commands":100,"prompt":110,"zipAvailable":55},[101,104,107],{"tool":102,"command":103},"npx","npx skills add iDorgham\u002FWorkspace-Factory --skill agent-email-inbox",{"tool":105,"command":106},"bunx","bunx skills add iDorgham\u002FWorkspace-Factory --skill agent-email-inbox",{"tool":108,"command":109},"pnpm","pnpm dlx skills add iDorgham\u002FWorkspace-Factory --skill agent-email-inbox","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_resend_agent_email_inbox\u002Fskill.md",[],[113,142,165,187,207,228],{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":122,"publishedAt":141},"01M71P8WVBMB2X3D24R0YT0B0E","vercel-react-view-transitions","Guide for implementing smooth, native-feeling animations using React's View Transition API (`\u003CViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter\u002Fexit of components, animate list reorder, implement directional (forward\u002Fback) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions view transitions, `startViewTransition`, `ViewTransition`, transition types, or asks about animating between UI states in React without third-party animation libraries.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_vercel_labs_react_view_transitions\u002Fskill.md",[119],{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[123,124,125,126,127,128,129,130,131,132,133,134,135,136,34,137,31,138,139,140],"react","vercel react view transitions","view","react view transitions","vercel","transitions","shared","back","Animation","css","types","states","element","implementing","components","route","create","libraries","2026-11-10 13:02:02",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":149,"publishedAt":141},"01M71P8TDGHZ289TSAC5VTG2EW","vercel-react-native-skills","React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_vercel_labs_react_native_skills\u002Fskill.md",[148],{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},[123,150,151,127,152,153,154,155,136,156,34,157,137,158,159,160,161,162,163,164],"react native","native","vercel react native skills","react native skills","modules","performance","apps","tasks","mobile","expo","list","mobile performance","platform","animations","for",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":172,"publishedAt":141},"01M71P8QK4JF5BYRGYDDGPZQG9","vercel-react-best-practices","React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React\u002FNext.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_vercel_labs_react_best_practices\u002Fskill.md",[171],{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},[123,173,174,127,175,155,176,34,177,157,137,178,179,180,181,182,183,184,185,186],"react best practices","vercel react best practices","best practices","writing","code","data fetching","react components","next","refactoring","data","patterns","Optimization","guidelines","triggers",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":194,"publishedAt":141},"01M71P8HVEA70K5N6ZVJCG2ED3","vercel-composition-patterns","React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_vercel_labs_composition_patterns\u002Fskill.md",[193],{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},[195,127,196,183,197,198,199,200,186,201,202,203,34,204,157,137,205,206,31,123],"Composition","composition patterns","vercel composition patterns","react composition patterns","react 19","render","boolean","component architecture","use","reusable","architecture","react composition",{"skillNo":208,"name":209,"nameZh":6,"description":210,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":211,"categories":212,"keywords":216,"publishedAt":141},"01M71P60XXKKP222ZFJRG7R373","supabase-postgres-best-practices","Postgres performance optimization and best practices from Supabase. Use this skill when writing, reviewing, or optimizing Postgres queries, schema designs, or database configurations.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_supabase_supabase_postgres_best_practices\u002Fskill.md",[213],{"categoryNo":214,"categoryName":215,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",[217,218,219,220,175,221,222,42,164,184,155,223,224,176,225,203,226,35,227,34],"postgres best practices","supabase","postgres","supabase postgres best practices","categories","postgres performance","correct","schema","impact","Rules","database",{"skillNo":229,"name":230,"nameZh":6,"description":231,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":232,"categories":233,"keywords":235,"publishedAt":47},"01M71P2ZCEJD0PKC458YTP1FJW","portable-text-serialization","Render and serialize Portable Text to React, Svelte, Vue, Astro, HTML, Markdown, and plain text. Use when implementing Portable Text rendering in any frontend framework, building custom serializers for non-standard block types, converting Portable Text to HTML strings server-side, converting Portable Text to Markdown, extracting plain text from Portable Text, or troubleshooting rendering issues with marks, blocks, lists, or custom types.","https:\u002F\u002Fgithub.com\u002FiDorgham\u002FWorkspace-Factory\u002Fblob\u002Fmaster\u002Ffactory\u002Flibrary\u002Fskills\u002Fofficial_sanity_io_portable_text_serialization\u002Fskill.md",[234],{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},[236,237,238,239,240,133,241,200,242,136,243,244,34,123,245,246,247,248,249,250],"text","portable text serialization","portable","serialization","extracting","strings","issues","frontend","svelte","sanity","astro","server","markdown","rendering","vue"]