feat(python-sdk): add webhook verification and event handling (#344)
* 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>
This commit is contained in:
@@ -74,6 +74,34 @@ class UseSend:
|
||||
self.domains = Domains(self)
|
||||
self.campaigns = Campaigns(self)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Webhooks
|
||||
# ------------------------------------------------------------------
|
||||
def webhooks(self, secret: str) -> "Webhooks":
|
||||
"""Create a Webhooks instance for verifying webhook signatures.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
secret:
|
||||
The webhook signing secret (starts with 'whsec_').
|
||||
|
||||
Returns
|
||||
-------
|
||||
Webhooks
|
||||
A Webhooks instance for verifying signatures and constructing events.
|
||||
|
||||
Example
|
||||
-------
|
||||
```python
|
||||
usesend = UseSend("us_12345")
|
||||
webhooks = usesend.webhooks("whsec_xxx")
|
||||
|
||||
# In your webhook handler
|
||||
event = webhooks.construct_event(body, headers=request.headers)
|
||||
```
|
||||
"""
|
||||
return Webhooks(secret)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Internal request helper
|
||||
# ------------------------------------------------------------------
|
||||
@@ -160,3 +188,4 @@ from .emails import Emails # noqa: E402 pylint: disable=wrong-import-position
|
||||
from .contacts import Contacts # noqa: E402 pylint: disable=wrong-import-position
|
||||
from .domains import Domains # type: ignore # noqa: E402
|
||||
from .campaigns import Campaigns # type: ignore # noqa: E402
|
||||
from .webhooks import Webhooks # noqa: E402 pylint: disable=wrong-import-position
|
||||
|
||||
Reference in New Issue
Block a user