Securing Webhooks
Every webhook request sent by Caspeco includes two headers you can use to verify its authenticity:
| Header | Description |
|---|---|
X-CASPECO-TIMESTAMP |
Unix timestamp (seconds) of when the request was sent |
X-CASPECO-SIGNATURE |
HMAC-SHA256 signature of the request, Base64 encoded |
How the Signature Is Computed
Section titled “How the Signature Is Computed”The signature is computed using the UTF-8 bytes of your HMAC secret string as the HMAC key — do not Base64-decode the secret before using it. The message input is:
{timestamp}\n{body}Where {timestamp} is the value of X-CASPECO-TIMESTAMP and {body} is the raw UTF-8 encoded JSON request body. The two parts are joined with a newline character (\n).
The HMAC key is the UTF-8 encoding of the secret string as stored — it is a Base64 string, but it is not decoded before use. Pass the secret string value directly as the key.
The resulting HMAC-SHA256 hash is Base64 encoded and sent in X-CASPECO-SIGNATURE.
Verifying the Signature
Section titled “Verifying the Signature”To validate an incoming request, recompute the signature on your side and compare it to the value in X-CASPECO-SIGNATURE. If they match, the request is genuine.
Here is an example in C#:
using System.Security.Cryptography;using System.Text;
bool VerifySignature(string secret, string timestamp, string body, string signature){ var msg = Encoding.UTF8.GetBytes($"{timestamp}\n{body}"); using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var expected = Convert.ToBase64String(hmac.ComputeHash(msg)); return expected == signature;}Replay Protection
Section titled “Replay Protection”The X-CASPECO-TIMESTAMP header can be used to guard against replay attacks. Reject requests where the timestamp is older than a reasonable threshold (e.g. 5 minutes) relative to your server’s current time.
Managing Your HMAC Secret
Section titled “Managing Your HMAC Secret”Your HMAC secret is tied to your dev partner configuration and can be rotated from the Integrations page. After rotating, update your endpoint immediately — requests signed with the old secret will fail validation.