Centralized notification delivery: email (AWS SES), templates, delivery tracking, unsubscribe handling,
and processing with retries. The API is fire-and-forget (acceptance is not final delivery).
See
services/notification-service/README.md
for module entry points.
Endpoint: POST /notifications/webhooks/ses — raw SNS JSON body.
Return 200 OK for all valid requests (including subscription confirmations and unknown message IDs).
Security: @PermitAll; authenticity via SNS signature verification only.
1. SNS message handling
SubscribeURL to confirm. No DB changes. Return 200.SigningCertURL, canonical string per AWS docs).
Reject 4xx if invalid.
Parse Message as JSON (SES event).
Extract mail.messageId, notificationType (Bounce | Complaint | Delivery).
If not Bounce/Complaint/Delivery, return 200 and skip.2. Linking to our notification
findByProviderMessageId(String) (and index on
notifications.provider_message_id if needed).
Look up by mail.messageId.
If not found, return 200 and skip (idempotent).3. Persisting outcome
DeliveryEvent.DELIVERED, NotificationStatus.DELIVERED; Bounce →
DeliveryEvent.BOUNCED, NotificationStatus.BOUNCED; Complaint →
DeliveryEvent.COMPLAINT, NotificationStatus.COMPLAINT.
Insert DeliveryStatusRecord; update NotificationRecord.status.
Idempotent where possible (e.g. same messageId + event type).4. Parsing & DTOs
Type, Message, MessageId, SigningCertURL, SubscribeURL, etc.
Inner SES event: notificationType, mail.messageId, bounce / complaint / delivery.
Parse Message as JSON; handle missing fields safely.5. Signature verification
SigningCertURL (HTTPS only).
Build canonical string per AWS SNS docs; verify signature.
4xx on failure.6. Config / AWS
7. LocalStack
messageId or disabled config → 200, no DB update.8. Testing
noreply@<domain.rootZone>) in dev,
or run awslocal ses verify-email-identity for a custom notification.ses.from-email.
Set aws.ses.endpoint to LocalStack.
Inspect sent emails: curl http://localhost:4566/_aws/ses.
SNS webhooks not supported; use polling or no-op for webhook endpoint.