$npx -y skills add MoizIbnYousaf/marketing-cli --skill agent-email-inboxUse 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
| 1 | # AI Agent Email Inbox |
| 2 | |
| 3 | Set up a secure email inbox that lets an AI agent receive and respond to emails, with protection against prompt injection and email-based attacks. |
| 4 | |
| 5 | **Core principle:** An AI agent's inbox is a potential attack vector. Malicious actors can email instructions that the agent might blindly follow. Security configuration is not optional — it's the first thing you implement, not the last. |
| 6 | |
| 7 | This skill is context-independent — it does not use `brand/` files and works identically in any project. |
| 8 | |
| 9 | ## On Activation |
| 10 | |
| 11 | 1. Ask the user which agent needs an email inbox and what framework they're using (Next.js, Express, etc.). |
| 12 | 2. Determine environment: local development or production deployment. |
| 13 | 3. Walk through domain setup (Resend-managed or custom). |
| 14 | 4. Set up webhook endpoint with signature verification. |
| 15 | 5. If local dev: configure tunneling. |
| 16 | 6. Implement security level — read [references/security-levels.md](references/security-levels.md) and present options to the user. |
| 17 | 7. Connect webhook to agent processing. |
| 18 | |
| 19 | **Output:** A configured webhook handler file, environment variable checklist, and security configuration. |
| 20 | |
| 21 | ## Architecture |
| 22 | |
| 23 | ``` |
| 24 | Sender → Email → Resend (MX) → Webhook → Your Server → AI Agent |
| 25 | ↓ |
| 26 | Security Validation |
| 27 | ↓ |
| 28 | Process or Reject |
| 29 | ``` |
| 30 | |
| 31 | ## Before You Start: Account & API Key Setup |
| 32 | |
| 33 | Ask the user: |
| 34 | - **New account just for the agent?** → Simpler setup, full account access is fine |
| 35 | - **Existing account with other projects?** → Use domain-scoped API keys to limit what the agent can access. Even if the key leaks, it can only send from one domain. |
| 36 | |
| 37 | > **Don't paste API keys in chat!** They'll persist in conversation history. Have the user write directly to `.env` or use a secrets manager. |
| 38 | |
| 39 | ## Domain Setup |
| 40 | |
| 41 | ### Option 1: Resend-Managed Domain (Recommended for Getting Started) |
| 42 | |
| 43 | Use your auto-generated address: `<anything>@<your-id>.resend.app`. No DNS configuration needed. |
| 44 | |
| 45 | ### Option 2: Custom Domain |
| 46 | |
| 47 | The user enables receiving in the Resend dashboard, then adds an MX record: |
| 48 | |
| 49 | | Setting | Value | |
| 50 | |---------|-------| |
| 51 | | **Type** | MX | |
| 52 | | **Host** | Your domain or subdomain (e.g., `agent.yourdomain.com`) | |
| 53 | | **Value** | Provided in Resend dashboard | |
| 54 | | **Priority** | 10 (lowest number takes precedence) | |
| 55 | |
| 56 | **Use a subdomain** (e.g., `agent.yourdomain.com`) to avoid disrupting existing email services on your root domain — otherwise all email routes to Resend. |
| 57 | |
| 58 | ## Webhook Setup |
| 59 | |
| 60 | The user registers a webhook in Resend dashboard (Webhooks → Add Webhook → select `email.received`). They need the endpoint URL you'll create and the signing secret for verification. |
| 61 | |
| 62 | ```typescript |
| 63 | // app/api/webhooks/email/route.ts (Next.js App Router) |
| 64 | import { Resend } from 'resend'; |
| 65 | import { NextRequest, NextResponse } from 'next/server'; |
| 66 | |
| 67 | const resend = new Resend(process.env.RESEND_API_KEY); |
| 68 | |
| 69 | export async function POST(req: NextRequest) { |
| 70 | try { |
| 71 | const payload = await req.text(); |
| 72 | |
| 73 | const event = resend.webhooks.verify({ |
| 74 | payload, |
| 75 | headers: { |
| 76 | 'svix-id': req.headers.get('svix-id'), |
| 77 | 'svix-timestamp': req.headers.get('svix-timestamp'), |
| 78 | 'svix-signature': req.headers.get('svix-signature'), |
| 79 | }, |
| 80 | secret: process.env.RESEND_WEBHOOK_SECRET, |
| 81 | }); |
| 82 | |
| 83 | if (event.type === 'email.received') { |
| 84 | const { data: email } = await resend.emails.receiving.get( |
| 85 | event.data.email_id |
| 86 | ); |
| 87 | // Security validation happens here (see Security Levels) |
| 88 | await processEmailForAgent(event.data, email); |
| 89 | } |
| 90 | |
| 91 | return new NextResponse('OK', { status: 200 }); |
| 92 | } catch (error) { |
| 93 | console.error('Webhook error:', error); |
| 94 | return new NextResponse('Error', { status: 400 }); |
| 95 | } |
| 96 | } |
| 97 | ``` |
| 98 | |
| 99 | Resend retries failed deliveries with exponential backoff over ~6 hours. Emails are stored even if webhooks fail. |
| 100 | |
| 101 | ## Local Development with Tunneling |
| 102 | |
| 103 | Your local server isn't accessible from the internet. Use tunneling to expose it: |
| 104 | |
| 105 | | Option | Persistent URL? | Cost | |
| 106 | |--------|----------------|------| |
| 107 | | **ngrok (paid)** | Yes (static subdomain) | $8/mo | |
| 108 | | **Cloudflare named tunnel** | Yes (your own domain) | Free | |
| 109 | | **ngrok (free)** | No (changes on restart) | Free | |
| 110 | | **VS Code Port Forwarding** | No (changes per session) | Free | |
| 111 | |
| 112 | For webhooks, persistent URLs matter — otherwise you re-register the URL every time th |