* 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.
- new edit button in /dev-settings
- new updateApiKey mutation in api router
- new edit dialog-component
- new update-function in api-service
- changed sorting of api-key query to avoid list items jumping after updates
* fix: sync suppression list removal with AWS SES (closes#324)
When removing an email from the suppression list, now also removes it from
AWS SES account-level suppression list across all regions where the team
has domains configured.
- Add deleteFromSesSuppressionList helper to ses.ts
- Update removeSuppression to query team domains for unique regions
- Use best-effort pattern: AWS failures don't block local DB deletion
- Handle NotFoundException gracefully (email not in SES list)
* fix: correct failure detection logic for SES suppression removal
deleteFromSesSuppressionList returns false on error (never throws),
so check for fulfilled promises with value === false instead of
rejected status.
* fix: account for rejected promises in SES suppression removal
Updated the filter logic for Promise.allSettled to include 'rejected'
status as well as 'fulfilled' with a 'false' value. This ensures that
any errors occurring before the try block in deleteFromSesSuppressionList
are correctly caught and logged.
* Correct API reference in Go package documentation
Updated description to reference the useSend API instead of Unsend API.
* Update documentation to reflect useSend branding
Added a issue to the package maintainer.
If maintainer isn't actively maintaining the package. Will fork it.
* fix(docs): remove community section and update Go SDK documentation
- Remove community section until content is ready.
- Update Go SDK docs to useSend implementation.
* 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>