* feat: support standard AWS env vars and default credential chain
Replace non-standard AWS_ACCESS_KEY / AWS_SECRET_KEY with the AWS-standard
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY. The old names are kept as
fallbacks in the runtimeEnv for backward compatibility.
Both vars are now optional. When omitted, the credentials object is not
passed to SESv2Client, STSClient, or SNSClient — the AWS SDK then falls
back to its default provider chain (IAM roles, ECS task roles, instance
profiles, etc.), which is the recommended approach for cloud-native deployments.
Closes#316
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* refactor: extract shared getAwsCredentialOptions helper and add partial-config guard
- Move the credential spread logic into a single credentials.ts helper
so SESv2Client, STSClient, and SNSClient all share one implementation
- Throw a clear error if only one of AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
is set, preventing silent fallback to the default provider chain with a
half-configured environment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: align AWS env vars in docker and docs
* fix: use alias import for AWS credentials helper
---------
Co-authored-by: purva <purvahk08@gmail.com>
Co-authored-by: Purva Kandalgaonkar <136103488+purva-8@users.noreply.github.com>
* feat: add customizable contact double opt-in flow
* test: add double opt-in service coverage
* fix: address review comments for double opt-in PR
- Make pending status conditional on doubleOptInEnabled flag
- Backfill legacy unsubscribeReason for reliable pending detection
- Add doubleOptInContent to contact book listing select
- Fix duplicate toast on DOI editor subject save failure
- Harden searchParams parsing against string[] values
- Make default DOI template use link mark for clickable URL
- Make public API create+update atomic via transaction
- Prevent contact upsert failure when DOI email send fails
- Fix empty string template variable replacement
Co-authored-by: opencode <opencode@anthropic.com>
* fix: harden double opt-in confirmation safeguards
Preserve explicit unsubscribe intent in DOI flows and prevent confirmation links from re-subscribing opted-out contacts. Also sanitize subscribe-page error messaging and use timing-safe hash comparison for link verification.
* ui stuff
* fix: require doubleOptInUrl in double opt-in templates
* feat: add configurable from address for double opt-in emails
* feat: add resend confirmation flow for pending contacts
* fix: move subscribe confirmation to explicit POST flow
* test: add contact book public API endpoint coverage
* docs: add double opt-in documentation and update OpenAPI spec
Add a user guide for the double opt-in feature covering setup, contact
statuses, email customization, template variables, and best practices.
Update the OpenAPI spec to include doubleOptIn fields in all contactBook
request/response schemas.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: opencode <opencode@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent premature webhook auto-disable and allow re-enable
Use persisted failure counters when deciding auto-disable status and restore dashboard re-enable flow so webhooks are not deactivated unexpectedly after reset.
* fix: count webhook failures per failed call
Only increment consecutive failure counters after a call exhausts retries, while keeping the 30-call auto-disable threshold and stale-state protection.
* fix(docs): correct webhook SDK package name (#363)
* test: isolate webhook unit suite from mailer deps
Mock limit service in webhook unit tests so Vitest does not resolve team-service and mailer paths requiring usesend-js during CI.
* feat: add web test framework with infra-backed suites
* fix: honor DATABASE_URL env in integration prepare script
* fix: apply web test review feedback
* fix: streamline web test infra lifecycle and workflow scope
* feat(python-sdk): add webhook verification and event handling
Add webhook support to the Python SDK matching the JS SDK implementation:
- Add Webhooks class with verify() and construct_event() methods
- Implement HMAC-SHA256 signature verification with timing-safe comparison
- Add timestamp validation with configurable tolerance (default 5 minutes)
- Add comprehensive webhook event types (18 events: email, contact, domain, test)
- Add WebhookVerificationError with typed error codes
- Export webhook constants (headers) and types
* fix(python-sdk): harden webhook parsing and typing
Normalize invalid UTF-8 webhook payloads to INVALID_BODY errors so verify() safely returns false, and narrow base email webhook event types to avoid discriminated-union overlap. Add regression tests for both paths.
* chore(python-sdk): bump package version to 0.2.9
* feat(python-sdk): add local webhook test example project
Add a runnable Flask receiver and signed webhook sender under packages/python-sdk/example, and link it from the Python SDK README for local verification.
---------
Co-authored-by: Claude <noreply@anthropic.com>
The warning and limit-reached notification emails were being sent
multiple times because of a race condition: concurrent workers could
both read the Redis cooldown key as empty (GET), both send emails,
then both set the key (SETEX). Replaced the non-atomic GET + SETEX
pattern with a single atomic SET ... NX EX that claims the cooldown
slot before any emails are sent. Also increased cooldown from 6 hours
to 24 hours so each notification is sent at most once per day.
https://claude.ai/code/session_01VBYXi5e64Vtq1cXWsfTYTw
Co-authored-by: Claude <noreply@anthropic.com>