Signature Verification
Webhook endpoints must verify the authenticity of incoming requests to prevent spoofing. Synapse verifies signatures for all webhook providers: Resend (Svix), Lemon Squeezy (HMAC-SHA256), and pyrx.payment (shared secret).
Verification Methods by Provider
| Provider | Endpoint | Method | Header(s) |
|---|---|---|---|
| Resend | POST /v1/webhooks/resend | Svix (HMAC-SHA256) | svix-id, svix-timestamp, svix-signature |
| Lemon Squeezy | POST /v1/webhooks/lemonsqueezy | HMAC-SHA256 | x-signature |
| pyrx.payment (deprecated) | POST /v1/webhooks/payment | Shared secret | X-Webhook-Secret |
Synapse uses Resend as the primary email provider. All email delivery webhooks are verified via Svix.
pyrx.payment (Deprecated)
pyrx.payment is deprecated. New workspaces use Lemon Squeezy. This section is preserved for legacy integrations.
Every webhook request from pyrx.payment includes an X-Webhook-Secret header containing the shared secret. Your handler should compare this against your configured webhook secret using a constant-time comparison.
Verification Logic
Python
JavaScript
Always use constant-time comparison (hmac.compare_digest in Python, crypto.timingSafeEqual in Node.js) to prevent timing attacks. Standard string comparison (==) leaks information about the secret through response timing.
Secret Rotation
To rotate your webhook secret without downtime, Synapse supports comma-separated secrets:
Rotation Steps
- Generate a new secret in the pyrx.payment dashboard
- Update your configuration to accept both old and new secrets (comma-separated)
- Deploy the configuration change
- Update pyrx.payment to use the new secret for outgoing webhooks
- Remove the old secret from your configuration after confirming all webhooks arrive with the new secret
Rotate webhook secrets every 90 days or immediately if you suspect a compromise. The comma-separated format ensures zero downtime during rotation.
Lemon Squeezy Webhook Verification
Lemon Squeezy signs webhook requests with HMAC-SHA256. The signature is in the x-signature header, computed over the raw request body.
Python
JavaScript
Resend Webhook Verification
Resend uses Svix for webhook signing. Three headers are included in every request:
| Header | Description |
|---|---|
svix-id | Unique message identifier |
svix-timestamp | Timestamp used in signature generation |
svix-signature | HMAC signature of the payload |
Python
JavaScript
Install the Svix SDK: pip install svix (Python) or npm install svix (Node.js). Resend uses Svix for webhook signing.
Best Practices
- Always verify before processing -- Never process a webhook payload without verifying the signature first.
- Return 200 quickly -- Verify the signature, enqueue the work, and return
200 OK. Do not perform slow processing synchronously in the webhook handler. - Handle duplicates -- Webhook providers may retry delivery. Design your handler to be idempotent.
- Log all webhook requests -- Store the raw payload and headers for debugging delivery issues.
- Monitor for failures -- Alert on repeated verification failures, which may indicate an attack or a misconfigured secret.