Agent skill
email-service
Email sending patterns for transactional and marketing emails. Use when implementing email sending, templates, SMTP configuration, or email services like SendGrid, Resend, or Mailgun.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/email-service
SKILL.md
Email Service Patterns
Setup (Resend — recommended)
# email.py
import resend
resend.api_key = settings.RESEND_API_KEY
async def send_email(
to: str | list[str],
subject: str,
html: str,
from_email: str = "[email protected]",
) -> str:
"""Send transactional email, return message ID."""
params = resend.Emails.SendParams(
from_=from_email,
to=[to] if isinstance(to, str) else to,
subject=subject,
html=html,
)
email = resend.Emails.send(params)
return email["id"]
// lib/email.ts
import { Resend } from 'resend'
const resend = new Resend(process.env.RESEND_API_KEY)
export async function sendEmail(params: {
to: string | string[]
subject: string
html: string
from?: string
}) {
const { data, error } = await resend.emails.send({
from: params.from ?? '[email protected]',
to: Array.isArray(params.to) ? params.to : [params.to],
subject: params.subject,
html: params.html,
})
if (error) throw new Error(error.message)
return data
}
Setup (SendGrid)
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
def send_email_sendgrid(to: str, subject: str, html: str):
message = Mail(
from_email="[email protected]",
to_emails=to,
subject=subject,
html_content=html,
)
client = SendGridAPIClient(settings.SENDGRID_API_KEY)
response = client.send(message)
return response.status_code
HTML Email Templates (React Email)
// emails/WelcomeEmail.tsx
import { Html, Head, Body, Container, Text, Button, Hr } from '@react-email/components'
interface WelcomeEmailProps {
username: string
verifyUrl: string
}
export function WelcomeEmail({ username, verifyUrl }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Body style={{ fontFamily: 'Arial, sans-serif', backgroundColor: '#f4f4f4' }}>
<Container style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
<Text style={{ fontSize: '24px', fontWeight: 'bold', color: '#333' }}>
Welcome, {username}! 👋
</Text>
<Text style={{ color: '#666', lineHeight: '1.6' }}>
Thanks for signing up. Please verify your email address to get started.
</Text>
<Button
href={verifyUrl}
style={{
backgroundColor: '#3b82f6',
color: '#fff',
padding: '12px 24px',
borderRadius: '6px',
textDecoration: 'none',
display: 'inline-block',
}}
>
Verify Email
</Button>
<Hr />
<Text style={{ color: '#999', fontSize: '12px' }}>
If you didn't create an account, you can safely ignore this email.
</Text>
</Container>
</Body>
</Html>
)
}
// Render to HTML for sending
import { render } from '@react-email/render'
const html = render(<WelcomeEmail username="Alice" verifyUrl="https://..." />)
Common Email Types
# email_service.py
from .templates import render_template
async def send_welcome_email(user_email: str, username: str, verify_token: str):
verify_url = f"{BASE_URL}/verify?token={verify_token}"
html = render_template("welcome.html", username=username, verify_url=verify_url)
await send_email(user_email, f"Welcome to MyApp, {username}!", html)
async def send_password_reset(email: str, reset_token: str):
reset_url = f"{BASE_URL}/reset-password?token={reset_token}"
html = render_template("password_reset.html", reset_url=reset_url)
await send_email(email, "Reset your password", html)
async def send_invoice(email: str, invoice_data: dict):
html = render_template("invoice.html", **invoice_data)
await send_email(email, f"Invoice #{invoice_data['invoice_number']}", html)
async def send_notification(email: str, title: str, message: str):
html = f"""
<div style="font-family: Arial; padding: 20px;">
<h2>{title}</h2>
<p>{message}</p>
</div>
"""
await send_email(email, title, html)
Jinja2 Templates (Python)
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("templates/emails"))
def render_template(template_name: str, **context) -> str:
template = env.get_template(template_name)
return template.render(**context)
<!-- templates/emails/welcome.html -->
<!DOCTYPE html>
<html>
<body style="font-family: Arial; background: #f4f4f4; padding: 20px;">
<div style="max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px;">
<h1 style="color: #1a1a1a;">Welcome, {{ username }}! 👋</h1>
<p style="color: #666;">Click below to verify your email:</p>
<a href="{{ verify_url }}"
style="background: #3b82f6; color: white; padding: 12px 24px; border-radius: 6px; text-decoration: none;">
Verify Email
</a>
<p style="color: #999; font-size: 12px; margin-top: 20px;">
This link expires in 24 hours.
</p>
</div>
</body>
</html>
Email Queue (Background with Celery)
from celery import Celery
celery = Celery("tasks", broker=settings.REDIS_URL)
@celery.task(bind=True, max_retries=3, default_retry_delay=60)
def send_email_task(self, to: str, subject: str, html: str):
try:
send_email_sync(to, subject, html)
except Exception as exc:
raise self.retry(exc=exc)
# Usage — don't block the request
send_email_task.delay(user.email, "Welcome!", welcome_html)
SMTP (nodemailer / standard SMTP)
import nodemailer from 'nodemailer'
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
})
export async function sendEmail(to: string, subject: string, html: string) {
await transporter.sendMail({
from: '"MyApp" <[email protected]>',
to,
subject,
html,
})
}
Rules
- Use a transactional email service (Resend, SendGrid, Mailgun) — NEVER raw SMTP in production
- Always queue emails (Celery/background task) — never send synchronously in request handler
- Verify sender domain with SPF, DKIM, DMARC records (deliverability)
- Never build HTML emails with
f-stringswith user input (XSS in email clients) - Include unsubscribe link in marketing emails (CAN-SPAM / GDPR)
- Test email rendering with Litmus or Email on Acid before launch
- Rate limit: max 1 email per minute per recipient to avoid spam classification
- Use separate API keys for transactional vs marketing emails
- Log email send attempts (ID, recipient, template, status) in your DB
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?