Why Verify Webhooks?
Security is critical. Anyone can send a POST request to your webhook endpoint. Without verification, malicious actors could:- Send fake events to corrupt your data
- Trigger unwanted actions in your application
- Cause your system to process fraudulent information
- Launch denial-of-service attacks
How AutoSend Signs Webhooks
Every webhook request from AutoSend includes anX-Webhook-Signature header containing an HMAC-SHA256 signature.
Signature Generation
AutoSend generates the signature using this process:- Format the webhook payload with the event type, timestamp, and event data
- Convert the payload to JSON string (the raw request body)
- Compute HMAC-SHA256 using your webhook secret as the key
- Convert to hexadecimal format
- Add as header:
X-Webhook-Signature: <signature>
Webhook Request Headers
Every webhook request includes these headers:"a1b2c3d4e5f6...""email.opened""delivery-123...""1699790400000"application/jsonExample: "application/json""AutoSend-Webhooks/1.0"Steps to Verify Signatures
Retrieving Your Webhook Secret
Your webhook secret is shown only once when you create the webhook. If you’ve lost it, you can retrieve it:Complete Production Example
Here’s a complete, production-ready webhook endpoint with signature verification, timestamp validation, and error handling:Security Best Practices
Always Use Constant-Time Comparison
Always Use Constant-Time Comparison
=== or == to compare signatures. Use constant-time comparison functions to prevent timing attacks:crypto.timingSafeEqual() function throws an error if the buffer lengths don’t match. Always wrap it in a try-catch block.Store Secrets Securely
Store Secrets Securely
- Environment variables (
.envfiles for local development) - Secure secret management services (AWS Secrets Manager, HashiCorp Vault, etc.)
- Encrypted configuration files
- Commit secrets to version control
- Include secrets in client-side code
- Share secrets in logs or error messages
- Use the same secret across multiple environments
Use the Raw Request Body
Use the Raw Request Body
Validate the Timestamp
Validate the Timestamp
X-Webhook-Timestamp header to reject old or replayed requests:Date.now().toString().Use HTTPS for Production
Use HTTPS for Production
- Requests are encrypted in transit
- Man-in-the-middle attacks are prevented
- Webhook data remains confidential
- Your webhook secret is protected
Respond Quickly (Under 10 Seconds)
Respond Quickly (Under 10 Seconds)
Handle Webhook Retries Idempotently
Handle Webhook Retries Idempotently
X-Webhook-Delivery-Id header to track which deliveries you’ve already processed.Rotate Secrets Periodically
Rotate Secrets Periodically
Create a new webhook with the same events and URL
Update your application to support both old and new secrets temporarily
Verify the new webhook is working correctly
Delete the old webhook
Remove the old secret from your application
Webhook Payload Structure
AutoSend sends webhook payloads in this format:"email.opened", "contact.created")Troubleshooting
Signature Verification Always Fails
Signature Verification Always Fails
-
Using the wrong secret
-
Body parsing issues
-
String encoding issues
-
Comparing wrong values
-
Secret contains whitespace
Timestamp Validation Fails
Timestamp Validation Fails
-
Wrong time unit - Timestamp is in milliseconds, not seconds
-
Clock skew - Server time is off
-
Timezone issues
Testing Signature Verification Locally
Testing Signature Verification Locally
Webhooks Not Being Received
Webhooks Not Being Received
-
Check webhook is active
- Navigate to Webhooks in AutoSend
- Verify webhook status is “Active”
- Check if failure count is high (auto-disabled after 5 failures)
-
Verify URL is accessible
-
Check delivery logs
- Click on your webhook in AutoSend
- View the “Delivery Logs” tab
- Look for error messages or status codes
-
Test with resend
- Create a test event
- Use the “Resend” feature to manually trigger delivery
- Check your server logs