Your API Key
Each webhook endpoint is assigned a unique API key by Baanx. This key is the shared secret used to generate and verify signatures. Key properties:- Generated and managed by Baanx — not client-generated
- Unique per webhook endpoint
- Can be rotated on request (or via the Rotate Key API)
- Custom keys can be configured in special cases
Request Headers
Every webhook request includes these headers:| Header | Description |
|---|---|
Content-Type | Always application/json |
X-Timestamp | Unix timestamp (seconds, UTC) when the request was sent |
X-Signature | HMAC-SHA256 signature as a lowercase hex string |
How the Signature is Calculated
Baanx constructs the signature as follows:X-Signature header.
Verification Steps
Check the timestamp
Reject the request if the timestamp is more than 300 seconds (5 minutes) away from the current server time. This prevents replay attacks.
Compute the expected signature
Calculate HMAC-SHA256 over the payload using your API key, then hex-encode the result (lowercase).
Code Examples
Replay Protection
TheX-Timestamp header protects against replay attacks — where an attacker captures a legitimate webhook request and re-sends it later.
Your endpoint should reject any request where the timestamp differs from the current server time by more than 5 minutes (300 seconds):
Common Verification Mistakes
Parsing the body before verifying
Parsing the body before verifying
If you parse the raw JSON into an object and then re-serialise it to compute the signature, the result may differ from the original raw string (different whitespace, key ordering). Always use the raw request body string from the HTTP request.
Using string equality instead of timing-safe comparison
Using string equality instead of timing-safe comparison
Using
=== to compare signatures leaks information through response time differences. Always use crypto.timingSafeEqual (Node.js) or hmac.compare_digest (Python).Not checking the timestamp
Not checking the timestamp
Without timestamp validation, a captured webhook request could be replayed indefinitely. Always reject requests where
| now - timestamp | > 300.