# Email Gateway

> |

- **Type:** Skill
- **Install:** `agentstack add skill-kgeminic-claude-skills-1-email-gateway`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Kgeminic](https://agentstack.voostack.com/s/kgeminic)
- **Installs:** 0
- **Category:** [Communication](https://agentstack.voostack.com/c/communication)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Kgeminic](https://github.com/Kgeminic)
- **Source:** https://github.com/Kgeminic/claude-skills-1/tree/main/skills/email-gateway

## Install

```sh
agentstack add skill-kgeminic-claude-skills-1-email-gateway
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Email Gateway (Multi-Provider)

**Status**: Production Ready ✅
**Last Updated**: 2026-01-10
**Providers**: Resend, SendGrid, Mailgun, SMTP2Go

---

## Quick Start

Choose your provider based on needs:

| Provider | Best For | Key Feature | Free Tier |
|----------|----------|-------------|-----------|
| **Resend** | Modern apps, React Email | JSX templates | 100/day, 3k/month |
| **SendGrid** | Enterprise scale | Dynamic templates | 100/day forever |
| **Mailgun** | Developer webhooks | Event tracking | 100/day |
| **SMTP2Go** | Reliable relay, AU | Simple API | 1k/month trial |

### Resend (Recommended for New Projects)

```typescript
const response = await fetch('https://api.resend.com/emails', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.RESEND_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'noreply@yourdomain.com',
    to: 'user@example.com',
    subject: 'Welcome!',
    html: 'Hello World',
  }),
});

const data = await response.json();
// { id: "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" }
```

### SendGrid (Enterprise)

```typescript
const response = await fetch('https://api.sendgrid.com/v3/mail/send', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.SENDGRID_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    personalizations: [{
      to: [{ email: 'user@example.com' }],
    }],
    from: { email: 'noreply@yourdomain.com' },
    subject: 'Welcome!',
    content: [{
      type: 'text/html',
      value: 'Hello World',
    }],
  }),
});

// Returns 202 on success (no body)
```

### Mailgun

```typescript
const formData = new FormData();
formData.append('from', 'noreply@yourdomain.com');
formData.append('to', 'user@example.com');
formData.append('subject', 'Welcome!');
formData.append('html', 'Hello World');

const response = await fetch(
  `https://api.mailgun.net/v3/${env.MAILGUN_DOMAIN}/messages`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${btoa(`api:${env.MAILGUN_API_KEY}`)}`,
    },
    body: formData,
  }
);

const data = await response.json();
// { id: "", message: "Queued. Thank you." }
```

### SMTP2Go

```typescript
const response = await fetch('https://api.smtp2go.com/v3/email/send', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    api_key: env.SMTP2GO_API_KEY,
    to: [''],
    sender: 'noreply@yourdomain.com',
    subject: 'Welcome!',
    html_body: 'Hello World',
  }),
});

const data = await response.json();
// { data: { succeeded: 1, failed: 0, email_id: "..." } }
```

---

## Provider Comparison

### Features

| Feature | Resend | SendGrid | Mailgun | SMTP2Go |
|---------|--------|----------|---------|---------|
| **React Email** | ✅ Native | ❌ | ❌ | ❌ |
| **Dynamic Templates** | ✅ | ✅ | ✅ | ✅ |
| **Batch Sending** | 50/request | 1000/request | 1000/request | 100/request |
| **Webhooks** | ✅ | ✅ | ✅ | ✅ |
| **SMTP** | ✅ | ✅ | ✅ | ✅ Primary |
| **IP Warmup** | Managed | Manual | Manual | Managed |
| **Dedicated IPs** | Enterprise | $90+/mo | $80+/mo | Custom |
| **Analytics** | Basic | Advanced | Advanced | Good |
| **A/B Testing** | ❌ | ✅ | ✅ | ❌ |

### Rate Limits (Free Tier)

| Provider | Daily | Monthly | Overage Cost |
|----------|-------|---------|--------------|
| **Resend** | 100 | 3,000 | $1/1k |
| **SendGrid** | 100 | Forever | $15 for 10k |
| **Mailgun** | 100 | Forever | $15 for 10k |
| **SMTP2Go** | ~33 | 1,000 trial | $10 for 10k |

### API Limits

| Provider | Requests/sec | Burst | Retry After Header |
|----------|--------------|-------|-------------------|
| **Resend** | 10 | Yes | ✅ |
| **SendGrid** | 600 | Yes | ✅ |
| **Mailgun** | Varies | Yes | ✅ |
| **SMTP2Go** | 10 | Limited | ✅ |

### Message Limits

| Provider | Max Size | Attachments | Max Recipients |
|----------|----------|-------------|----------------|
| **Resend** | 40 MB | 40 MB total | 50/request |
| **SendGrid** | 20 MB | 20 MB total | 1000/request |
| **Mailgun** | 25 MB | 25 MB total | 1000/request |
| **SMTP2Go** | 50 MB | 50 MB total | 100/request |

---

## Configuration

### Environment Variables

```bash
# Resend
RESEND_API_KEY=re_xxxxxxxxx

# SendGrid
SENDGRID_API_KEY=SG.xxxxxxxxx

# Mailgun
MAILGUN_API_KEY=xxxxxxxx-xxxxxxxx-xxxxxxxx
MAILGUN_DOMAIN=mg.yourdomain.com
MAILGUN_REGION=us  # or eu

# SMTP2Go
SMTP2GO_API_KEY=api-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

### Wrangler Secrets (Cloudflare Workers)

```bash
# Set secrets
echo "re_xxxxxxxxx" | npx wrangler secret put RESEND_API_KEY
echo "SG.xxxxxxxxx" | npx wrangler secret put SENDGRID_API_KEY
echo "xxxxxxxx-xxxxxxxx-xxxxxxxx" | npx wrangler secret put MAILGUN_API_KEY
echo "api-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | npx wrangler secret put SMTP2GO_API_KEY

# Deploy to activate
npx wrangler deploy
```

### TypeScript Types

```typescript
// Resend
interface ResendEmail {
  from: string;
  to: string | string[];
  subject: string;
  html?: string;
  text?: string;
  cc?: string | string[];
  bcc?: string | string[];
  replyTo?: string | string[];
  headers?: Record;
  attachments?: Array;
  tags?: Record;
  scheduledAt?: string; // ISO 8601
}

interface ResendResponse {
  id: string;
}

// SendGrid
interface SendGridEmail {
  personalizations: Array;
    cc?: Array;
    bcc?: Array;
    subject?: string;
    dynamic_template_data?: Record;
  }>;
  from: { email: string; name?: string };
  subject?: string;
  content?: Array;
  template_id?: string;
  attachments?: Array;
}

// Mailgun
interface MailgunEmail {
  from: string;
  to: string | string[];
  subject: string;
  html?: string;
  text?: string;
  cc?: string | string[];
  bcc?: string | string[];
  'h:Reply-To'?: string;
  template?: string;
  'h:X-Mailgun-Variables'?: string; // JSON
  attachment?: File | File[];
  inline?: File | File[];
  'o:tag'?: string | string[];
  'o:tracking'?: 'yes' | 'no';
  'o:tracking-clicks'?: 'yes' | 'no' | 'htmlonly';
  'o:tracking-opens'?: 'yes' | 'no';
}

interface MailgunResponse {
  id: string;
  message: string;
}

// SMTP2Go
interface SMTP2GoEmail {
  api_key: string;
  to: string[];
  sender: string;
  subject: string;
  html_body?: string;
  text_body?: string;
  custom_headers?: Array;
  attachments?: Array;
}

interface SMTP2GoResponse {
  data: {
    succeeded: number;
    failed: number;
    failures?: string[];
    email_id?: string;
  };
}
```

---

## Common Patterns

### 1. Transactional Emails

**Password Reset**:

```typescript
// templates/password-reset.ts
export async function sendPasswordReset(
  provider: 'resend' | 'sendgrid' | 'mailgun' | 'smtp2go',
  to: string,
  resetToken: string,
  env: Env
): Promise {
  const resetUrl = `https://yourapp.com/reset-password?token=${resetToken}`;

  const html = `
    Reset Your Password
    Click the link below to reset your password:
    Reset Password
    This link expires in 1 hour.
  `;

  switch (provider) {
    case 'resend':
      return sendViaResend(to, 'Reset Your Password', html, env);
    case 'sendgrid':
      return sendViaSendGrid(to, 'Reset Your Password', html, env);
    case 'mailgun':
      return sendViaMailgun(to, 'Reset Your Password', html, env);
    case 'smtp2go':
      return sendViaSMTP2Go(to, 'Reset Your Password', html, env);
  }
}

async function sendViaResend(
  to: string,
  subject: string,
  html: string,
  env: Env
): Promise {
  const response = await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${env.RESEND_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from: 'noreply@yourdomain.com',
      to,
      subject,
      html,
    }),
  });

  if (!response.ok) {
    const error = await response.text();
    return { success: false, error };
  }

  const data = await response.json();
  return { success: true, id: data.id };
}
```

### 2. Batch Sending

**Resend (max 50 recipients)**:

```typescript
async function sendBatchResend(
  recipients: string[],
  subject: string,
  html: string,
  env: Env
): Promise> {
  const results: Array = [];

  // Chunk into groups of 50
  for (let i = 0; i  results.push({ email, id: data.id }));
    } else {
      const error = await response.text();
      chunk.forEach(email => results.push({ email, error }));
    }
  }

  return results;
}
```

**SendGrid (max 1000 personalizations)**:

```typescript
async function sendBatchSendGrid(
  recipients: Array }>,
  templateId: string,
  env: Env
): Promise {
  const response = await fetch('https://api.sendgrid.com/v3/mail/send', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${env.SENDGRID_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      personalizations: recipients.map(r => ({
        to: [{ email: r.email, name: r.name }],
        dynamic_template_data: r.data || {},
      })),
      from: { email: 'noreply@yourdomain.com' },
      template_id: templateId,
    }),
  });

  if (!response.ok) {
    const error = await response.text();
    return { success: false, error };
  }

  return { success: true };
}
```

### 3. React Email Templates (Resend Only)

**Install React Email**:

```bash
npm install react-email @react-email/components
```

**Create Template**:

```tsx
// emails/welcome.tsx
import {
  Html,
  Head,
  Body,
  Container,
  Heading,
  Text,
  Button,
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  confirmUrl: string;
}

export default function WelcomeEmail({ name, confirmUrl }: WelcomeEmailProps) {
  return (
    
      
      
        
          Welcome, {name}!
          Thanks for signing up. Please confirm your email address:
          
            Confirm Email
          
        
      
    
  );
}
```

**Send via Resend SDK (Node.js)**:

```typescript
import { Resend } from 'resend';
import WelcomeEmail from './emails/welcome';

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

await resend.emails.send({
  from: 'noreply@yourdomain.com',
  to: 'user@example.com',
  subject: 'Welcome!',
  react: WelcomeEmail({ name: 'Alice', confirmUrl: 'https://...' }),
});
```

**Send via Workers (render to HTML first)**:

```typescript
import { render } from '@react-email/render';
import WelcomeEmail from './emails/welcome';

const html = render(WelcomeEmail({ name: 'Alice', confirmUrl: 'https://...' }));

await fetch('https://api.resend.com/emails', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.RESEND_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'noreply@yourdomain.com',
    to: 'user@example.com',
    subject: 'Welcome!',
    html,
  }),
});
```

### 4. Dynamic Templates

**SendGrid**:

```typescript
// 1. Create template in SendGrid dashboard with handlebars
// Subject: Welcome {{name}}!
// Body: Hi {{name}}Your code: {{confirmationCode}}

// 2. Send with template ID
const response = await fetch('https://api.sendgrid.com/v3/mail/send', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.SENDGRID_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    personalizations: [{
      to: [{ email: 'user@example.com' }],
      dynamic_template_data: {
        name: 'Alice',
        confirmationCode: 'ABC123',
      },
    }],
    from: { email: 'noreply@yourdomain.com' },
    template_id: 'd-xxxxxxxxxxxxxxxxxxxxxxxx',
  }),
});
```

**Mailgun**:

```typescript
// 1. Create template in Mailgun dashboard or via API
// Use {{name}} and {{confirmationCode}} variables

// 2. Send with template name
const formData = new FormData();
formData.append('from', 'noreply@yourdomain.com');
formData.append('to', 'user@example.com');
formData.append('subject', 'Welcome');
formData.append('template', 'welcome-template');
formData.append('h:X-Mailgun-Variables', JSON.stringify({
  name: 'Alice',
  confirmationCode: 'ABC123',
}));

const response = await fetch(
  `https://api.mailgun.net/v3/${env.MAILGUN_DOMAIN}/messages`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${btoa(`api:${env.MAILGUN_API_KEY}`)}`,
    },
    body: formData,
  }
);
```

### 5. Attachments

**Resend**:

```typescript
const fileBuffer = await file.arrayBuffer();
const base64Content = btoa(String.fromCharCode(...new Uint8Array(fileBuffer)));

await fetch('https://api.resend.com/emails', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.RESEND_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'noreply@yourdomain.com',
    to: 'user@example.com',
    subject: 'Your Invoice',
    html: 'Attached is your invoice.',
    attachments: [{
      filename: 'invoice.pdf',
      content: base64Content,
    }],
  }),
});
```

**SendGrid**:

```typescript
const fileBuffer = await file.arrayBuffer();
const base64Content = btoa(String.fromCharCode(...new Uint8Array(fileBuffer)));

const response = await fetch('https://api.sendgrid.com/v3/mail/send', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${env.SENDGRID_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    personalizations: [{
      to: [{ email: 'user@example.com' }],
    }],
    from: { email: 'noreply@yourdomain.com' },
    subject: 'Your Invoice',
    content: [{ type: 'text/html', value: 'Attached is your invoice.' }],
    attachments: [{
      content: base64Content,
      filename: 'invoice.pdf',
      type: 'application/pdf',
      disposition: 'attachment',
    }],
  }),
});
```

**Mailgun** (uses FormData with File):

```typescript
const formData = new FormData();
formData.append('from', 'noreply@yourdomain.com');
formData.append('to', 'user@example.com');
formData.append('subject', 'Your Invoice');
formData.append('html', 'Attached is your invoice.');
formData.append('attachment', file); // File object directly

const response = await fetch(
  `https://api.mailgun.net/v3/${env.MAILGUN_DOMAIN}/messages`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${btoa(`api:${env.MAILGUN_API_KEY}`)}`,
    },
    body: formData,
  }
);
```

### 6. Webhooks (Event Tracking)

**Resend Webhooks**:

Events: `email.sent`, `email.delivered`, `email.delivery_delayed`, `email.bounced`, `email.complained`, `email.opened`, `email.clicked`

```typescript
// Verify webhook signature
import { createHmac } from 'crypto';

export async function verifyResendWebhook(
  request: Request,
  secret: string
): Promise {
  const signature = request.headers.get('svix-signature');
  const timestamp = request.headers.get('svix-timestamp');
  const body = await request.text();

  if (!signature || !timestamp) return false;

  const signedContent = `${timestamp}.${body}`;
  const expectedSignature = createHmac('sha256', secret)
    .update(signedContent)
    .digest('base64');

  return signature.includes(expectedSignature);
}

// Handle webhook
export async function handleResendWebhook(request: Request, env: Env) {
  const isValid = await verifyResendWebhook(request, env.RESEND_WEBHOOK_SECRET);
  if (!isValid) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event = await request.json();

  switch (event.type) {
    case 'email.bounced':
      // Mark email as invalid
      await markEmailInvalid(event.data.email);
      break;
    case 'email.complained':
      // Unsubscribe user
      await unsubscribeUser(event.data.email);
      break;
  }

  return new Response('OK');
}
```

**SendGrid Webhooks**:

```typescript
// Verify webhook signature (requires express-style body parser)
import { EventWebhook, EventWebhookHeader } from '@sendgrid/eventwebhook';

export async function verifySendGridWebhook(
  request: Request,
  publicKey: string
): Promise {
  const signature = request.headers.get(EventWebhookHeader.SIGNATURE());
  const timestamp = request.headers.get(EventWebhookHeader.TIMESTAMP());
  const body = a

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Kgeminic](https://github.com/Kgeminic)
- **Source:** [Kgeminic/claude-skills-1](https://github.com/Kgeminic/claude-skills-1)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-kgeminic-claude-skills-1-email-gateway
- Seller: https://agentstack.voostack.com/s/kgeminic
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
