Interactive SES deliverability troubleshooting. Diagnoses bounces, spam placement, send failures, account health, and complaint issues with targeted AWS CLI commands and ordered remediation steps.
Interactive diagnostician for AWS SES email delivery problems. When a user reports an email issue, identify the symptom, run targeted diagnostics, correlate findings, and provide ordered remediation.
You are an interactive diagnostician. Do NOT dump all sections at the user. Instead:
Ask which problem they're experiencing, then follow the matching branch:
Before running diagnostics, gather the user's domain and region from Wraps metadata:
# Read connection metadata to get domain, region, and config
cat ~/.wraps/connections/*.json
Metadata JSON paths:
.region (top-level).services.email.config.domain.provider.accountId.services.email.preset.services.email.config.mailFromSubdomainThen check AWS connectivity:
aws sts get-caller-identity
email checkThe Wraps CLI includes a comprehensive deliverability checker. Always use --json for structured output that's easy to interpret programmatically:
npx @wraps.dev/cli email check <domain> --json
This single command checks all of the following:
all mechanismselector1/2/3), key type and sizeKey JSON paths in the response:
.score.grade, .score.finalScore, .score.deductions[].spf.exists, .spf.valid, .spf.record, .spf.lookupCount, .spf.allMechanism.dkim.found, .dkim.selectors[].valid, .dkim.selectors[].keyBits.dmarc.exists, .dmarc.valid, .dmarc.policy, .dmarc.reportingEnabled.mx.exists, .mx.records[], .mx.hasRedundancy.blacklist.overallClean, .blacklist.ipChecks.listed[], .blacklist.domainChecks.listed[].domainAge.ageInDays, .domainAge.createdAtGrade thresholds:
Use this command as the first diagnostic step for any symptom involving DNS, authentication, or reputation. It replaces the need for individual dig, nslookup, or manual DNS commands.
For account-level checks, run get-account once and interpret multiple fields:
aws sesv2 get-account --region <region>
Key fields to inspect from the single response:
| Field | What it tells you |
|---|---|
SendingEnabled |
Whether the account can send at all |
ProductionAccessEnabled |
false = sandbox mode (200/day, verified recipients only) |
EnforcementStatus |
HEALTHY, PROBATION, or SHUTDOWN |
Details.ReviewDetails.Status |
PENDING, GRANTED, DENIED, or FAILED |
SendQuota.Max24HourSend |
Daily sending limit |
SendQuota.SentLast24Hours |
Emails sent in rolling 24h window |
SendQuota.MaxSendRate |
Max emails per second |
SuppressionAttributes.SuppressedReasons |
What gets auto-suppressed |
Important: get-account does NOT return bounce/complaint rate percentages directly. Those are available in the SES console's Reputation Dashboard or via Virtual Deliverability Manager metrics. The thresholds below are for interpreting those rates when the user provides them or when checking the console.
npx @wraps.dev/cli email check <domain> --json
Look at .score.grade and .score.deductions[] for DNS/authentication issues that could cause bounces.
aws sesv2 get-account --region <region>
Check EnforcementStatus and compare SentLast24Hours to Max24HourSend. If near the limit, sends are throttled or rejected.
Bounce rate thresholds (from SES console Reputation Dashboard):
| Rate | Status | Action |
|---|---|---|
| < 2% | Healthy | Investigate individual cases |
| 2-5% | Warning | Clean list, check recent imports |
| 5-10% | Critical | Stop marketing sends, aggressive cleanup |
| > 10% | Danger | SES will suspend account |
# List all suppressed addresses
aws sesv2 list-suppressed-destinations --region <region>
# Filter by bounce vs complaint
aws sesv2 list-suppressed-destinations --region <region> --reasons BOUNCE
aws sesv2 list-suppressed-destinations --region <region> --reasons COMPLAINT
If suppression list is large, it often indicates a bad import or list hygiene issue. To remove a false positive:
aws sesv2 delete-suppressed-destination \
--email-address user@example.com --region <region>
email check findingsnpx @wraps.dev/cli email check <domain> --json
Interpret the JSON response - key things to look for:
SPF (.spf): Must include amazonses.com. Check .spf.allMechanism — ~ (soft fail) is the recommendation for a sending domain; DMARC does the enforcing. - (hard fail) is valid but rejects forwarded mail before DKIM is checked, so only prefer it when DMARC is already at quarantine/reject — or on a parked domain that never sends.
DKIM (.dkim): At least one valid selector with 2048-bit RSA key. The CLI automatically checks the correct SES token-based selectors (e.g., bsksdtrd66emmvw6frf33yopnumfs33r._domainkey), NOT the generic selector1/2/3 names.
DMARC (.dmarc): Policy should be quarantine or reject (NOT none). If .dmarc.policy is none, inbox providers may deprioritize emails. Recommended record:
_dmarc.<domain> TXT "v=DMARC1; p=reject; rua=mailto:dmarc@<domain>"
Blacklists (.blacklist): Check .blacklist.overallClean. If false, inspect .blacklist.ipChecks.listed[] and .blacklist.domainChecks.listed[]. Note the priority field — high priority listings are critical, low priority (like cbl.anti-spam.org.cn) are less impactful but should still be addressed.
Domain Age (.domainAge.ageInDays): Domains < 30 days old have inherently low reputation.
Check the MAIL FROM configuration from the SES identity:
aws sesv2 get-email-identity --email-identity <domain> --region <region>
Look at MailFromAttributes:
MailFromDomain: Should be a subdomain like mail.<domain>MailFromDomainStatus: Should be SUCCESSIf MAIL FROM is not configured, set it up with these DNS records:
mail.<domain> MX 10 feedback-smtp.<region>.amazonses.commail.<domain> TXT "v=spf1 include:amazonses.com ~all"aws sesv2 get-account --region <region>
Check EnforcementStatus (HEALTHY, PROBATION, SHUTDOWN).
For new domains (< 30 days old), low reputation is expected. Follow warming schedule:
Ask the user for a recent email's HTML or subject line. Check for:
email check deductionsdelistUrl from listed entries, or visit the blacklist's websiteaws sesv2 get-account --region <region>
Check all of these from the single response:
ProductionAccessEnabled: If false, account is in sandbox mode (200 emails/day, verified recipients only). Fix: Request production access in AWS Console → SES → Account Dashboard.SendQuota: If SentLast24Hours equals or exceeds Max24HourSend, all sends are rejected until the 24-hour window rolls.SendingEnabled: If false, sending is disabled entirely.EnforcementStatus: If not HEALTHY, account may be restricted.aws sesv2 get-email-identity --email-identity <domain> --region <region>
Check VerifiedForSendingStatus. If false, the domain isn't verified:
npx @wraps.dev/cli email check <domain> --json to see DKIM statusnpx @wraps.dev/cli email verify --domain <domain>npx @wraps.dev/cli email status
The IAM role must have ses:SendEmail and ses:SendRawEmail permissions. If using Vercel OIDC, verify the trust relationship is configured correctly.
aws sesv2 get-configuration-set \
--configuration-set-name wraps-email-tracking --region <region>
If this returns a 404, the configuration set is missing. Check event destinations:
aws sesv2 get-configuration-set-event-destinations \
--configuration-set-name wraps-email-tracking --region <region>
email check, wait for propagationnpx @wraps.dev/cli email init to recreate/fix IAM rolenpx @wraps.dev/cli email upgrade to recreateaws sesv2 get-account --region <region>
Key fields:
EnforcementStatus: HEALTHY, PROBATION, or SHUTDOWNDetails.ReviewDetails: Any ongoing AWS reviewProductionAccessEnabled: Whether production sending is allowedFrom the same get-account response, note the enforcement status. For actual bounce/complaint rate percentages, check the SES console Reputation Dashboard or Virtual Deliverability Manager.
| Metric | Healthy | Warning | Critical |
|---|---|---|---|
| Bounce Rate | < 2% | 2-5% | > 5% |
| Complaint Rate | < 0.1% | 0.1-0.3% | > 0.3% |
npx @wraps.dev/cli email check <domain> --json
Check for blacklist listings, domain age, and authentication issues that could be contributing to account health problems.
aws cloudwatch describe-alarms \
--alarm-name-prefix wraps --region <region>
Check if any alarms are in ALARM state.
If PROBATION:
If SHUTDOWN:
aws sesv2 get-account --region <region>
Check EnforcementStatus. For actual complaint rate, check SES console Reputation Dashboard.
Complaint rate thresholds:
| Rate | Status | Action |
|---|---|---|
| < 0.1% | Healthy | Continue normally |
| 0.1-0.3% | Warning | Review content and targeting |
| 0.3-0.5% | Critical | Pause marketing emails |
| > 0.5% | Danger | SES may suspend account |
aws sesv2 list-suppressed-destinations --region <region> --reasons COMPLAINT
High complaint suppression count indicates content or targeting problems.
Verify emails include:
List-Unsubscribe header (RFC 8058)List-Unsubscribe-Post: List-Unsubscribe=One-Click headeraws sesv2 get-account --region <region>
Check SendQuota.MaxSendRate. If you're sending faster than this (emails per second), SES throttles delivery.
If Wraps event tracking is enabled, check for DELIVERY_DELAY events in the DynamoDB table. Common causes:
Are sends happening in bursts? Large bursts trigger:
Problems are often related. Common patterns:
Pattern: Bad import cascade
Pattern: Missing authentication cascade
Pattern: New domain cold start
When reporting findings, always note if multiple issues are related and prioritize the root cause fix.