Skip to content

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

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.

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;
}

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.

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.