agent-email-inbox
Configures a secure email inbox for AI agents, enabling real-time email processing while preventing security vulnerabilities.
Install this skill
Security score
The agent-email-inbox skill was audited on May 24, 2026 and we found 153 security issues across 4 threat categories, including 20 high-severity. Review the findings below before installing.
Categories Tested
Security Issues
Template literal with variable interpolation in command context
| 162 | console.log(`Rejected email from unauthorized sender: ${sender}`); |
Template literal with variable interpolation in command context
| 198 | console.log(`Rejected email from unauthorized domain: ${eventData.from}`); |
Template literal with variable interpolation in command context
| 271 | console.warn(`Potential injection attempt from ${eventData.from}:`, analysis.matches); |
Template literal with variable interpolation in command context
| 551 | console.log(`Rejected email from unauthorized sender: ${sender}`); |
Template literal with variable interpolation in command context
| 768 | const message = ` |
Template literal with variable interpolation in command context
| 830 | subject: subject.startsWith('Re:') ? subject : `Re: ${subject}`, |
Template literal with variable interpolation in command context
| 836 | throw new Error(`Failed to send: ${error.message}`); |
Template literal with variable interpolation in command context
| 913 | console.log(`[SECURITY] Rejected email from ${event.data.from}: ${reason}`, details); |
Template literal with variable interpolation in command context
| 920 | subject: `[Agent] Rejected email: ${reason}`, |
Template literal with variable interpolation in command context
| 921 | text: ` |
Curl to non-GitHub URL
| 975 | 1. Server is running: `curl http://localhost:3000` should return a response |
Curl to non-GitHub URL
| 976 | 2. Tunnel is working: `curl https://<your-tunnel-url>` should return the same response |
Curl to non-GitHub URL
| 1018 | 3. Check the tunnel is running: `curl https://<your-tunnel-url>` |
Webhook reference - potential data exfiltration
| 3 | description: Use when setting up an email inbox for an AI agent (Moltbot, Clawdbot, or similar) - configuring inbound email, webhooks, tunneling for local development, and implementing security measur |
Webhook reference - potential data exfiltration
| 8 | - name: RESEND_WEBHOOK_SECRET |
Webhook reference - potential data exfiltration
| 9 | description: Webhook signing secret for verifying inbound email event payloads. Found in the Resend dashboard under Webhooks. |
Webhook reference - potential data exfiltration
| 21 | ### Why Webhook-Based Receiving? |
Webhook reference - potential data exfiltration
| 23 | Resend uses webhooks for inbound email, meaning your agent is notified **instantly** when an email arrives. This is valuable for agents because: |
Webhook reference - potential data exfiltration
| 35 | Sender → Email → Resend (MX) → Webhook → Your Server → AI Agent |
Webhook reference - potential data exfiltration
| 44 | This 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 |
Webhook reference - potential data exfiltration
| 63 | 3. **Create webhook endpoint** - Handle `email.received` events with security built in from the start |
Webhook reference - potential data exfiltration
| 140 | **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 reference - potential data exfiltration
| 403 | | Verify webhook signatures | Prevents spoofed webhook events | |
Webhook reference - potential data exfiltration
| 453 | ## Webhook Setup |
Webhook reference - potential data exfiltration
| 457 | After choosing your security level and setting up your domain, create a webhook endpoint. This will allow you to be notified when new emails are received. |
Webhook reference - potential data exfiltration
| 460 | 1. Go to https://resend.com/webhooks (the Webhooks tab of the dashboard) |
Webhook reference - potential data exfiltration
| 461 | 2. Click "Add webhook" |
Webhook reference - potential data exfiltration
| 465 | 6. Once it's created, you need the webhook signing secret in order to verify the webhook. They can find that by clicking on the webhook in the Webhooks dashboard and copying the text under "Signing Se |
Webhook reference - potential data exfiltration
| 471 | Your webhook endpoint receives notifications when emails arrive. |
Webhook reference - potential data exfiltration
| 473 | > **Critical: Use raw body for verification.** Webhook signature verification requires the raw request body. If you parse it as JSON before verifying, the signature check will fail. |
Webhook reference - potential data exfiltration
| 475 | > - **Express:** Use `express.raw({ type: 'application/json' })` on the webhook route (not `express.json()`) |
Webhook reference - potential data exfiltration
| 480 | // app/api/webhooks/email/route.ts |
Webhook reference - potential data exfiltration
| 491 | // Verify webhook signature |
Webhook reference - potential data exfiltration
| 492 | const event = resend.webhooks.verify({ |
Webhook reference - potential data exfiltration
| 499 | secret: process.env.RESEND_WEBHOOK_SECRET, |
Webhook reference - potential data exfiltration
| 503 | // Webhook payload only includes metadata, not email body |
Webhook reference - potential data exfiltration
| 515 | console.error('Webhook error:', error); |
Webhook reference - potential data exfiltration
| 530 | // CRITICAL: Use express.raw, NOT express.json, for the webhook route |
Webhook reference - potential data exfiltration
| 531 | app.post('/webhook/email', express.raw({ type: 'application/json' }), async (req, res) => { |
Webhook reference - potential data exfiltration
| 535 | // Verify webhook signature |
Webhook reference - potential data exfiltration
| 536 | const event = resend.webhooks.verify({ |
Webhook reference - potential data exfiltration
| 543 | secret: process.env.RESEND_WEBHOOK_SECRET, |
Webhook reference - potential data exfiltration
| 557 | // Webhook payload only includes metadata, not email body |
Webhook reference - potential data exfiltration
| 565 | console.error('Webhook error:', error); |
Webhook reference - potential data exfiltration
| 575 | app.listen(3000, () => console.log('Webhook server running on :3000')); |
Webhook reference - potential data exfiltration
| 578 | #### Webhook Verification Fallback (Svix) |
Webhook reference - potential data exfiltration
| 580 | If you're using an older Resend SDK that doesn't have `resend.webhooks.verify()`, you can verify signatures directly with the `svix` package: |
Webhook reference - potential data exfiltration
| 587 | import { Webhook } from 'svix'; |
Webhook reference - potential data exfiltration
| 589 | // Replace resend.webhooks.verify() with: |
Webhook reference - potential data exfiltration
| 590 | const wh = new Webhook(process.env.RESEND_WEBHOOK_SECRET); |
Webhook reference - potential data exfiltration
| 598 | ### Register Webhook in Resend Dashboard |
Webhook reference - potential data exfiltration
| 600 | 1. Go to Dashboard → Webhooks → Add Webhook |
Webhook reference - potential data exfiltration
| 603 | 4. Copy the signing secret to `RESEND_WEBHOOK_SECRET` |
Webhook reference - potential data exfiltration
| 605 | ### Webhook Retry Behavior |
Webhook reference - potential data exfiltration
| 607 | Resend automatically retries failed webhook deliveries with exponential backoff: |
Webhook reference - potential data exfiltration
| 610 | - Failed deliveries are visible in the Webhooks dashboard |
Webhook reference - potential data exfiltration
| 611 | - Emails are stored even if webhooks fail — you won't lose messages |
Webhook reference - potential data exfiltration
| 615 | Your local server isn't accessible from the internet. Use tunneling to expose it for webhook delivery. |
Webhook reference - potential data exfiltration
| 619 | > Webhook URLs are registered in Resend's dashboard. If your tunnel URL changes (e.g., ngrok restart), you must update the webhook configuration manually. For development, this is manageable. For anyt |
Webhook reference - potential data exfiltration
| 631 | - Must update webhook URL in Resend dashboard after each restart |
Webhook reference - potential data exfiltration
| 656 | Cloudflare Tunnels can be either quick (ephemeral) or named (persistent). For webhooks, use **named tunnels**. |
Webhook reference - potential data exfiltration
| 658 | **Quick tunnel (ephemeral - NOT recommended for webhooks):** |
Webhook reference - potential data exfiltration
| 673 | cloudflared tunnel create my-agent-webhook |
Webhook reference - potential data exfiltration
| 681 | - hostname: webhook.yourdomain.com |
Webhook reference - potential data exfiltration
| 686 | cloudflared tunnel route dns my-agent-webhook webhook.yourdomain.com |
Webhook reference - potential data exfiltration
| 689 | cloudflared tunnel run my-agent-webhook |
Webhook reference - potential data exfiltration
| 692 | Now `https://webhook.yourdomain.com` always points to your local machine, even across restarts. |
Webhook reference - potential data exfiltration
| 707 | **Note:** URL changes each VS Code session. Not suitable for persistent webhooks. |
Webhook reference - potential data exfiltration
| 719 | ### Webhook URL Configuration |
Webhook reference - potential data exfiltration
| 722 | - Development: `https://<tunnel-url>/api/webhooks/email` |
Webhook reference - potential data exfiltration
| 723 | - Production: `https://yourdomain.com/api/webhooks/email` |
Webhook reference - potential data exfiltration
| 727 | For a reliable agent inbox, deploy your webhook endpoint to production infrastructure instead of relying on tunnels. |
Webhook reference - potential data exfiltration
| 731 | **Option A: Deploy webhook handler to serverless** |
Webhook reference - potential data exfiltration
| 737 | - Your webhook handler runs alongside your agent |
Webhook reference - potential data exfiltration
| 743 | - Add webhook route to existing web server |
Webhook reference - potential data exfiltration
| 748 | # In your Next.js project with the webhook handler |
Webhook reference - potential data exfiltration
| 751 | # Your webhook URL becomes: |
Webhook reference - potential data exfiltration
| 752 | # https://your-project.vercel.app/api/webhooks/email |
Webhook reference - potential data exfiltration
| 757 | See the Express example in the Webhook Setup section above. Deploy it with a reverse proxy (nginx, caddy) for HTTPS, or behind a load balancer that terminates SSL. |
Webhook reference - potential data exfiltration
| 761 | ### Webhook Gateway (Recommended) |
Webhook reference - potential data exfiltration
| 763 | The best way to connect email to Clawdbot is via the webhook gateway. This takes full advantage of Resend's webhook functionality, delivering emails to your agent in real time — no polling delays, no |
Webhook reference - potential data exfiltration
| 783 | Clawdbot can poll the Resend API for new emails during heartbeats. This is simpler to set up but does not take advantage of Resend's webhook functionality — emails are not delivered in real time, and |
Webhook reference - potential data exfiltration
| 805 | For deep integration, implement Clawdbot's external channel plugin interface to treat email as a first-class channel alongside Telegram, Signal, etc. This also uses webhooks for real-time delivery. |
Webhook reference - potential data exfiltration
| 860 | event: EmailReceivedWebhookEvent |
Webhook reference - potential data exfiltration
| 909 | event: EmailReceivedWebhookEvent, |
Webhook reference - potential data exfiltration
| 941 | RESEND_WEBHOOK_SECRET=whsec_xxxxxxxxx |
Webhook reference - potential data exfiltration
| 955 | | Trusting email headers | Use webhook verification, not email headers for auth | |
Webhook reference - potential data exfiltration
| 962 | | Using `express.json()` on webhook route | Use `express.raw({ type: 'application/json' })` — JSON parsing breaks signature verification | |
Webhook reference - potential data exfiltration
| 964 | | Old Resend SDK version | `emails.receiving.get()` and `webhooks.verify()` require recent SDK versions — see SDK Version Requirements | |
Webhook reference - potential data exfiltration
| 977 | 3. Webhook is active: Check status in Resend dashboard → Webhooks |
Webhook reference - potential data exfiltration
| 984 | **Cause:** Resend SDK version too old — `resend.webhooks.verify()` was added in recent versions. |
Webhook reference - potential data exfiltration
| 989 | Or use the Svix fallback (see Webhook Verification Fallback section above). |
Webhook reference - potential data exfiltration
| 1001 | ### Webhook returns 400 errors |
Webhook reference - potential data exfiltration
| 1004 | 1. **Wrong signing secret** — Check the Resend dashboard for the correct secret. Click on your webhook and copy "Signing Secret" from the upper right. |
Webhook reference - potential data exfiltration
| 1005 | 2. **Body parsing issue** — You must use the raw body for verification. Use `express.raw({ type: 'application/json' })` on the webhook route, not `express.json()`. |
Webhook reference - potential data exfiltration
| 1011 | **Fix:** Restart ngrok, then update the webhook URL in the Resend dashboard. |
Webhook reference - potential data exfiltration
| 1014 | ### Email received but no webhook fires |
Webhook reference - potential data exfiltration
| 1016 | 1. Check the webhook is "Active" in Resend dashboard → Webhooks |
Webhook reference - potential data exfiltration
| 1017 | 2. Check the endpoint URL is correct (including the path, e.g., `/webhook/email`) |
Webhook reference - potential data exfiltration
| 1019 | 4. Check the "Recent Deliveries" section on your webhook for status codes |
Webhook reference - potential data exfiltration
| 1029 | **This is expected behavior.** The webhook delivers a notification to the user, who then instructs the agent how to respond. This is the safest approach — the user reviews each email before the agent |
Ngrok tunnel reference
| 64 | 4. **Set up tunneling** (local dev) - Use ngrok or similar to expose your endpoint |
Ngrok tunnel reference
| 467 | To provide them the endpoint URL for step #3, you need to set up an endpoint, and then use tunneling with a tool like ngrok. |
Ngrok tunnel reference
| 469 | Resend requires these URLs to be https, and verifies certificates, so ensure that your ngrok setup includes a verified cert. |
Ngrok tunnel reference
| 619 | > Webhook URLs are registered in Resend's dashboard. If your tunnel URL changes (e.g., ngrok restart), you must update the webhook configuration manually. For development, this is manageable. For anyt |
Ngrok tunnel reference
| 620 | > - A **paid tunnel service** with static URLs (ngrok paid, Cloudflare named tunnels) |
Ngrok tunnel reference
| 625 | ### ngrok (Recommended) |
Ngrok tunnel reference
| 627 | The most popular and simplest tunneling solution. Use ngrok as the default choice for local development. |
Ngrok tunnel reference
| 630 | - URLs are random and change on every restart (e.g., `https://a1b2c3d4.ngrok-free.app`) |
Ngrok tunnel reference
| 635 | - Static subdomain that persists across restarts (e.g., `https://myagent.ngrok.io`) |
Ngrok tunnel reference
| 637 | - Recommended if using ngrok long-term |
Ngrok tunnel reference
| 641 | brew install ngrok # macOS |
Ngrok tunnel reference
| 642 | # or download from https://ngrok.com |
Ngrok tunnel reference
| 645 | ngrok config add-authtoken <your-token> |
Ngrok tunnel reference
| 648 | ngrok http 3000 |
Ngrok tunnel reference
| 651 | ngrok http --domain=myagent.ngrok.io 3000 |
Ngrok tunnel reference
| 661 | # URL changes every time - same problem as free ngrok |
Ngrok tunnel reference
| 695 | **Cons:** Requires owning a domain on Cloudflare, more setup than ngrok |
Ngrok tunnel reference
| 717 | **Note:** URLs change on restart. Same limitations as free ngrok. |
Ngrok tunnel reference
| 961 | | Using ephemeral tunnel URLs | Use persistent URLs (paid ngrok, Cloudflare named tunnels) or deploy to production | |
Ngrok tunnel reference
| 1008 | ### ngrok connection refused / tunnel died |
Ngrok tunnel reference
| 1010 | **Cause:** Free ngrok tunnels time out and change URLs on restart. |
Ngrok tunnel reference
| 1011 | **Fix:** Restart ngrok, then update the webhook URL in the Resend dashboard. |
Ngrok tunnel reference
| 1012 | **Better:** Use paid ngrok with a static domain, or deploy to production. |
Access to hidden dotfiles in home directory
| 676 | # Create config file ~/.cloudflared/config.yml |
Access to .env file
| 84 | - Human creates `.env` file directly: `echo "RESEND_API_KEY=re_xxx" >> .env` |
Access to .env file
| 484 | const resend = new Resend(process.env.RESEND_API_KEY); |
Access to .env file
| 499 | secret: process.env.RESEND_WEBHOOK_SECRET, |
Access to .env file
| 528 | const resend = new Resend(process.env.RESEND_API_KEY); |
Access to .env file
| 543 | secret: process.env.RESEND_WEBHOOK_SECRET, |
Access to .env file
| 590 | const wh = new Webhook(process.env.RESEND_WEBHOOK_SECRET); |
Access to .env file
| 814 | const resend = new Resend(process.env.RESEND_API_KEY); |
Access to .env file
| 849 | const resend = new Resend(process.env.RESEND_API_KEY); |
Access to .env file
| 853 | allowedSenders: (process.env.ALLOWED_SENDERS || '').split(',').filter(Boolean), |
Access to .env file
| 854 | allowedDomains: (process.env.ALLOWED_DOMAINS || '').split(',').filter(Boolean), |
Access to .env file
| 855 | securityLevel: process.env.SECURITY_LEVEL || 'strict', // 'strict' | 'domain' | 'filtered' | 'sandboxed' |
Access to .env file
| 856 | ownerEmail: process.env.OWNER_EMAIL, |
External URL reference
| 6 | description: Resend API key for sending and receiving emails. Get yours at https://resend.com/api-keys |
External URL reference
| 134 | **Tip:** To verify your DNS records have propagated correctly, visit [dns.email](https://dns.email) and input your domain. This tool checks MX, SPF, DKIM, and DMARC records all in one place. |
External URL reference
| 248 | // See: https://owasp.org/www-project-top-10-for-large-language-model-applications/ |
External URL reference
| 460 | 1. Go to https://resend.com/webhooks (the Webhooks tab of the dashboard) |
External URL reference
| 630 | - URLs are random and change on every restart (e.g., `https://a1b2c3d4.ngrok-free.app`) |
External URL reference
| 635 | - Static subdomain that persists across restarts (e.g., `https://myagent.ngrok.io`) |
External URL reference
| 642 | # or download from https://ngrok.com |
External URL reference
| 660 | cloudflared tunnel --url http://localhost:3000 |
External URL reference
| 682 | service: http://localhost:3000 |
External URL reference
| 692 | Now `https://webhook.yourdomain.com` always points to your local machine, even across restarts. |
External URL reference
| 722 | - Development: `https://<tunnel-url>/api/webhooks/email` |
External URL reference
| 723 | - Production: `https://yourdomain.com/api/webhooks/email` |
External URL reference
| 752 | # https://your-project.vercel.app/api/webhooks/email |
External URL reference
| 975 | 1. Server is running: `curl http://localhost:3000` should return a response |
External URL reference
| 976 | 2. Tunnel is working: `curl https://<your-tunnel-url>` should return the same response |
External URL reference
| 1018 | 3. Check the tunnel is running: `curl https://<your-tunnel-url>` |