Webhooks Security & Validation
Secure your webhook endpoints by verifying that each request genuinely came from Catena and hasn’t been tampered with. Every webhook includes cryptographic signatures and comprehensive headers for authentication, tracking, and event routing.
Security Critical: Always verify webhook signatures before processing events. Unverified webhooks expose your system to spoofing and tampering attacks.
Request Headers
Every webhook request includes HTTP headers for security verification, event metadata, and delivery tracking. Here’s what a typical webhook request looks like with all headers included:
Header Reference
Duplicate Detection: Store processed X-Request-ID values to detect and skip duplicate deliveries during retries.
Signature Verification
Catena signs every webhook using HMAC-SHA256 to ensure authenticity. The signature covers the timestamp and request body, preventing tampering and replay attacks.
Signing Key Management
When creating a webhook subscription, you can either provide your own signing key or let Catena generate one:
Provide Your Own Key
Include your secret key in the subscription request for full control over key management.
Catena-Generated Key
Omit the secret field and Catena will generate a secure key returned once in the creation response.
The generated secret is shown in full only in the initial creation response. In all subsequent API responses, the secret is masked as ***** for security.
Store Securely: The generated secret is shown only once. Store it securely in your secrets manager.
Key Rotation
If you lose your key or need to rotate it for security, use the webhook update endpoint to set a new secret.
Lost Your Secret?
If you’ve lost your signing secret, you cannot retrieve it — it’s masked as ***** in all API responses after creation. To recover, rotate to a new secret using the update endpoint:
Update your endpoint to use the new secret immediately after — deliveries will begin failing signature verification as soon as the rotation takes effect.
How Signatures Work
Each webhook includes two security headers:
X-Catena-Signature— Base64-encoded HMAC-SHA256 digestX-Catena-Timestamp— RFC 3339 timestamp (e.g.,2024-01-15T10:30:00+00:00)
The signature is computed as:
Verification Steps
Implement signature verification in four steps:
Extract headers and body
Get the signature headers and raw request body:
X-Catena-Timestamp— The timestamp used in signature computationX-Catena-Signature— The expected signature- Uncompressed payload — Decompress the gzipped request body to get the JSON string
Decompress First: Webhooks are always sent with Content-Encoding: gzip. You must decompress the raw request bytes to get the JSON string used for signature verification.
Check timestamp freshness
Parse the timestamp and verify it’s recent:
- Parse
X-Catena-Timestampas RFC 3339 / ISO 8601 - Reject requests older than 5 minutes to prevent replay attacks
- Account for clock skew between servers
Compare signatures securely
Use constant-time comparison to prevent timing attacks:
- Compare
X-Catena-Signaturewith your computed signature - Use timing-safe comparison (e.g.,
hmac.compare_digestin Python) - Respond with 401 Unauthorized if verification fails
- Respond with 400 Bad Request if headers are missing/malformed
Implementation Example
Security Best Practices
Troubleshooting
Signature verification always fails
Common causes:
- Using parsed JSON instead of uncompressed payload string
- Not decompressing the payload before verification
- Incorrect webhook secret
- Character encoding issues (ensure UTF-8)
Solution: Ensure you’re using the uncompressed JSON string. Decompress the raw gzipped bytes first.
Intermittent verification failures
Common causes:
- Clock skew between servers
- Timestamp tolerance too strict
- Network delays causing stale timestamps
Solution: Allow a 5-minute tolerance window and ensure your server clock is synchronized via NTP.
Missing headers
Common causes:
- Proxy or load balancer stripping headers
- Case-sensitive header lookups
- Framework normalizing header names
Solution: Check your infrastructure configuration. Headers may be lowercase (x-catena-signature) depending on your framework.