Skip to main content
Production-grade email delivery using AWS Simple Email Service (SES) with bounce handling, suppression management, and observability.

Architecture

Truthlocks uses AWS SES for all transactional emails. The integration includes:
  • SES Provider: Direct integration with SES v2 API for sending emails
  • SNS Webhooks: Real-time bounce/complaint notifications via SNS topics
  • Suppression List: Automatic suppression of hard bounces and complaints
  • Environment Gating: Log-only mode for development, SES for production

AWS SES Setup

1. Verify Domain

Verify your sending domain in SES console:
# Using AWS CLI
aws ses verify-domain-identity --domain truthlocks.com --region us-east-1

# Get DKIM tokens (add to DNS)
aws ses verify-domain-dkim --domain truthlocks.com --region us-east-1

2. Configure DNS Records

Add the following records to your domain DNS:
TypeNameValue
TXT_amazonses.truthlocks.com(verification token from SES)
CNAME(DKIM selector 1)(DKIM value from SES)
MXmail.truthlocks.comfeedback-smtp.us-east-1.amazonses.com
TXTmail.truthlocks.comv=spf1 include:amazonses.com ~all

3. Request Production Access

New SES accounts are in sandbox mode. Request production access in the SES console to send emails to unverified addresses.

Environment Variables

VariableRequiredDescription
EMAIL_MODEYeslog (dev) or ses (prod)
SES_REGIONIf sesAWS region (e.g., us-east-1)
SES_FROMIf sesSender address (e.g., no-reply@truthlocks.com)
In ENV=production, the service will fail to start if SES_REGION and SES_FROM are not configured.

Bounce & Complaint Handling

SES notifications are received via an SNS webhook at /v1/notifications/ses-events.

Event Types

  • Bounce (Permanent): Hard bounce → Email suppressed automatically
  • Bounce (Transient): Soft bounce → Logged but not suppressed
  • Complaint: User marked as spam → Email suppressed automatically
  • Delivery: Successful delivery → Logged for observability

Suppression List Management

Suppressed emails are stored in the email_suppressions table and will not receive future emails. The API returns EMAIL_SUPPRESSED error code.
-- Check if email is suppressed
SELECT * FROM email_suppressions WHERE email = 'user@example.com';

-- View recent suppressions
SELECT email, reason, suppressed_at
FROM email_suppressions
ORDER BY suppressed_at DESC
LIMIT 20;

Troubleshooting

Email not received

  1. Check logs for email_sent or email_send_failed events
  2. Verify the recipient email is not in email_suppressions
  3. Check SES console for sending quota and reputation
  4. Verify DKIM/SPF records are correctly configured

Observability

All email operations are logged with structured fields:
{
  "level": "info",
  "msg": "email_sent",
  "message_id": "0102...",
  "to": "user@example.com",
  "subject": "Welcome to Truthlocks",
  "template_name": "issuer-onboarding-invite",
  "duration_ms": 245
}