AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Email Gateway

skill-kgeminic-claude-skills-1-email-gateway · by Kgeminic

|

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add skill-kgeminic-claude-skills-1-email-gateway

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-kgeminic-claude-skills-1-email-gateway)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Email Gateway? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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)

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)

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

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

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

# 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)

# 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

// 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:

// 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):

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):

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:

npm install react-email @react-email/components

Create Template:

// 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):

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):

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:

// 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:

// 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:

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:

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):

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

// 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:

// 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.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.