wraps-team/skills/aws-ses-best-practices

Claude Code and agent skill by wraps-team/skills

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Email deliverability, warming, compliance, and operational guidelines for AWS Simple Email Service.

Install

npx vskill@latest install wraps-team/skills/aws-ses-best-practices

SKILL.md

AWS SES Best Practices

Email deliverability, warming, compliance, and operational guidelines for AWS Simple Email Service.

Email Authentication

SPF (Sender Policy Framework)

Add to your domain's DNS:

v=spf1 include:amazonses.com ~all

For custom MAIL FROM domain:

# TXT record for mail.yourapp.com
v=spf1 include:amazonses.com ~all
DKIM (DomainKeys Identified Mail)

SES provides three CNAME records for DKIM. Add all three:

# Example (actual values from SES console)
selector1._domainkey.yourapp.com CNAME selector1.dkim.amazonses.com
selector2._domainkey.yourapp.com CNAME selector2.dkim.amazonses.com
selector3._domainkey.yourapp.com CNAME selector3.dkim.amazonses.com
DMARC (Domain-based Message Authentication)

Add TXT record to your domain:

# Start with monitoring mode
_dmarc.yourapp.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@yourapp.com"
# After monitoring, move to quarantine
_dmarc.yourapp.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourapp.com"
# Production: reject unauthenticated emails
_dmarc.yourapp.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc@yourapp.com"
Custom MAIL FROM Domain

Improves deliverability by using your domain instead of amazonses.com:

# MX record for mail.yourapp.com
mail.yourapp.com MX 10 feedback-smtp.us-east-1.amazonses.com
# SPF record for mail.yourapp.com
mail.yourapp.com TXT "v=spf1 include:amazonses.com ~all"

IP Warming

New SES accounts have limited sending reputation. Warm up gradually.

Warming Schedule
DayDaily VolumeNotes
1-2200Start small, monitor bounces
3-4500Check complaint rate
5-71,000Monitor reputation dashboard
8-145,000Steady increase
15-2110,000
22-3025,000
30+50,000+Scale as needed
Warming Best Practices
  1. Start with engaged users — Send to users who recently opened/clicked
  2. Prioritize transactional — Welcome emails, password resets have high engagement
  3. Avoid cold lists — Don't send to addresses that haven't engaged in 6+ months
  4. Monitor daily — Check bounce/complaint rates in SES dashboard
  5. Slow down if issues — If bounces > 5% or complaints > 0.1%, reduce volume

Bounce Handling

Types of Bounces

Hard Bounces (Permanent):

  • Invalid/non-existent email address
  • Domain doesn't exist
  • Action: Remove immediately, never send again

Soft Bounces (Temporary):

  • Mailbox full
  • Server temporarily unavailable
  • Action: Retry with exponential backoff, remove after 3-5 attempts

Transient Bounces:

  • Auto-responders
  • Challenge-response systems
  • Action: Generally ignore, don't count against reputation
Bounce Rate Thresholds
RateStatusAction
< 2%HealthyContinue normally
2-5%WarningInvestigate, clean list
5-10%CriticalStop sends, clean list aggressively
> 10%DangerSES may suspend account
Handling Bounces with Wraps
typescript
// Wraps CLI sets up automatic bounce handling via EventBridge → SQS → Lambda → DynamoDB
// Query bounced addresses (schema shown is illustrative — the real table's
// key is messageId/sentAt with an accountId-sentAt-index GSI, not pk/sk;
// verify the actual query shape in packages/cli/src/infrastructure/resources/dynamodb.ts
// before copying this example):
import { DynamoDBClient, QueryCommand } from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({});
const bounces = await client.send(new QueryCommand({
TableName: 'wraps-email-history',
KeyConditionExpression: 'pk = :pk',
ExpressionAttributeValues: {
':pk': { S: 'BOUNCE#hard' },
},
}));

Complaint Handling

Complaint Rate Thresholds
RateStatusAction
< 0.1%HealthyContinue normally
0.1-0.3%WarningReview content, add unsubscribe
0.3-0.5%CriticalPause marketing emails
> 0.5%DangerSES may suspend account
Reducing Complaints
  1. Clear unsubscribe — One-click unsubscribe in every email
  2. Set expectations — Tell users what/when you'll email during signup
  3. Honor preferences — Let users choose email types/frequency
  4. Clean lists — Remove unengaged users proactively
  5. Relevant content — Only send what users signed up for
List-Unsubscribe Header

Add to all marketing emails:

typescript
// With raw email or custom headers
const headers = {
'List-Unsubscribe': '<mailto:unsubscribe@yourapp.com>, <https://yourapp.com/unsubscribe>',
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
};

List Hygiene

Email Validation

Validate emails at signup:

typescript
// Basic format validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Check for common typos
const typoSuggestions: Record<string, string> = {
'gmial.com': 'gmail.com',
'gmal.com': 'gmail.com',
'hotmal.com': 'hotmail.com',
'yaho.com': 'yahoo.com',
};
// Use email verification service for important signups
// (e.g., ZeroBounce, NeverBounce, Hunter)
Engagement-Based Cleanup

Remove or suppress addresses based on engagement:

Last EngagementAction
< 30 daysActive, send normally
30-90 daysReduce frequency
90-180 daysRe-engagement campaign
180+ daysSuppress from regular sends
365+ daysRemove from list
Suppression Lists

Maintain lists of addresses to never email:

typescript
// Hard bounces - permanent suppression
// Complaints - permanent suppression
// Unsubscribes - honor indefinitely
// Role addresses - suppress (admin@, info@, support@)

Content Best Practices

Subject Lines
  • Keep it short — Under 50 characters
  • Be specific — Tell them what's inside
  • Avoid spam triggers — No ALL CAPS, excessive punctuation, "FREE!!!"
  • Personalize — Include name or relevant detail
Email Body
  1. Text version — Always include plain text alternative
  2. Image-to-text ratio — Keep images < 40% of content
  3. Hosted images — Use absolute URLs, not embedded images
  4. Alt text — Every image needs alt text
  5. Mobile-friendly — Single column, 600px max width
  6. Clear CTA — One primary call-to-action
Avoid Spam Triggers

Words to avoid:

  • FREE, WINNER, CONGRATULATIONS
  • Act now, Limited time, Urgent
  • $$, Make money, Cash bonus
  • Click here, Buy now

Formatting to avoid:

  • ALL CAPS
  • Excessive punctuation!!!
  • Red text
  • Large fonts
  • Invisible text (white on white)

Sending Patterns

Consistent Schedule
  • Send at consistent times (users learn to expect your emails)
  • Avoid sudden volume spikes (looks like spam)
  • Spread large sends over time (don't blast all at once)
Time Zone Awareness
typescript
// Send at optimal local time
const sendAtLocalTime = (email: string, preferredHour: number) => {
const userTimezone = getUserTimezone(email); // from user preferences
const now = new Date();
const targetTime = new Date(now.toLocaleString('en-US', { timeZone: userTimezone }));
targetTime.setHours(preferredHour, 0, 0, 0);
if (targetTime < now) {
targetTime.setDate(targetTime.getDate() + 1);
}
return targetTime;
};
Throttling for Large Lists
typescript
// Don't send 100k emails at once
const BATCH_SIZE = 1000;
const DELAY_BETWEEN_BATCHES_MS = 60000; // 1 minute
async function sendBulk(recipients: string[], template: string) {
for (let i = 0; i < recipients.length; i += BATCH_SIZE) {
const batch = recipients.slice(i, i + BATCH_SIZE);
await email.sendBulkTemplate({
from: 'hello@yourapp.com',
template,
destinations: batch.map(to => ({ to, templateData: {} })),
});
if (i + BATCH_SIZE < recipients.length) {
await sleep(DELAY_BETWEEN_BATCHES_MS);
}
}
}

Monitoring & Alerts

Key Metrics to Track
MetricTargetAlert Threshold
Bounce Rate< 2%> 5%
Complaint Rate< 0.1%> 0.3%
Delivery Rate> 95%< 90%
Open Rate> 20%< 10%
Click Rate> 2%< 1%
CloudWatch Alarms

Wraps CLI sets up basic alarms. Add custom ones:

typescript
// High bounce rate alarm
const alarm = new cloudwatch.Alarm(this, 'HighBounceRate', {
metric: new cloudwatch.Metric({
namespace: 'AWS/SES',
metricName: 'Bounce',
statistic: 'Sum',
period: Duration.hours(1),
}),
threshold: 50,
evaluationPeriods: 1,
comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
});
SES Reputation Dashboard

Check regularly in AWS Console → SES → Reputation Metrics:

  • Account status (Healthy, Under review, Paused)
  • Bounce rate trend
  • Complaint rate trend

Compliance

CAN-SPAM (US)
  1. Accurate headers — From/reply-to must be accurate
  2. No deceptive subjects — Subject must reflect content
  3. Physical address — Include valid postal address
  4. Unsubscribe — Clear, working unsubscribe mechanism
  5. Honor opt-outs — Process within 10 business days
GDPR (EU)
  1. Consent — Clear, explicit opt-in for marketing
  2. Right to access — Provide data on request
  3. Right to erasure — Delete data on request
  4. Data portability — Export data in common format
  5. Lawful basis — Document why you're processing data
CASL (Canada)
  1. Express consent — Written/verbal permission required
  2. Implied consent — Limited (existing relationship, published email)
  3. Unsubscribe — Honor within 10 days
  4. Identification — Sender identity and contact info
Footer Template
html
<footer style="font-size: 12px; color: #666;">
<p>
You're receiving this email because you signed up at yourapp.com.
</p>
<p>
<a href="{{unsubscribe_url}}">Unsubscribe</a> |
<a href="{{preferences_url}}">Email Preferences</a>
</p>
<p>
Your Company, Inc.<br>
123 Main Street<br>
City, State 12345
</p>
</footer>

SES Limits & Quotas

Default Limits (Sandbox)
  • 200 emails/24 hours
  • 1 email/second
  • Can only send to verified addresses
Production Limits
  • Varies based on account history
  • Start at 50,000/day, 14/second
  • Request increases as needed
Requesting Limit Increase
  1. AWS Console → SES → Account Dashboard
  2. Click "Request Production Access" or "Request Limit Increase"
  3. Provide:
  • Use case description
  • Expected volume
  • Bounce/complaint handling process
  • List collection method

Troubleshooting

"Email not delivered"
  1. Check SES console for bounces/complaints
  2. Verify recipient didn't unsubscribe
  3. Check spam folder
  4. Test with mail-tester.com
"High bounce rate"
  1. Validate email addresses at signup
  2. Remove old addresses (180+ days inactive)
  3. Use double opt-in
  4. Check for typos in bulk imports
"High complaint rate"
  1. Add clear unsubscribe link
  2. Review email content
  3. Reduce frequency
  4. Segment engaged vs unengaged users
"Emails going to spam"
  1. Verify SPF, DKIM, DMARC are set up
  2. Check content for spam triggers
  3. Warm up sending volume gradually
  4. Build engagement (opens/clicks improve reputation)

AWS CLI Diagnostics

Account Status
bash
# Get sending quota and daily usage
aws ses get-send-quota
# Check if in sandbox (SESv2)
aws sesv2 get-account
# Account-level suppression settings
aws sesv2 get-account --query 'SuppressionAttributes'
Identity Verification
bash
# List verified domains
aws ses list-identities --identity-type Domain
# List verified email addresses
aws ses list-identities --identity-type EmailAddress
# Check verification status for a domain
aws ses get-identity-verification-attributes \
--identities yourapp.com
# Full identity details (SESv2)
aws sesv2 get-email-identity --email-identity yourapp.com
DKIM Status

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
1
7-day trend score
3

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 23 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/wraps-team/skills/aws-ses-best-practices/badge)](https://verified-skill.com/skills/wraps-team/skills/aws-ses-best-practices)

Something wrong with this skill or its page? Tell us.