While using our official libraries is the recommended and most straightforward method to verify webhooks (as discussed in the previous verification guide), there may be situations where you need to verify them manually. This section explains the manual verification process in detail.

The recommended way to verify webhooks is using our official libraries. Please consider that implementation strategy first.

Verifying Signatures Manually

Each webhook call includes three headers that are crucial for verifying authenticity:

  • webhook-id: A unique message identifier for the webhook message. This value is unique across all messages. If a webhook is resent (due to a previous failure), the webhook-id will remain the same.

  • webhook-timestamp: The timestamp of the attempt in seconds since the Unix epoch.

  • webhook-signature: A Base64-encoded list of signatures (space-delimited).

You will use these headers, along with the raw request body, to generate and compare the expected signature.

Constructing the Signed Content

The content to sign is created by concatenating the id, timestamp, and payload (raw body of the request) with a . character between them:

It’s critical that you use the raw request body here. Any modifications (such as re-stringifying a parsed JSON body) will change the content and result in a signature mismatch.

Determining the Expected Signature

Straddle uses an HMAC with SHA-256 to sign its webhooks.

To calculate the expected signature:

  1. Extract the portion of your signing secret after the whsec_ prefix.

  2. Base64-decode that portion to get your key bytes.

  3. Compute the HMAC using the decoded key and the signedContent.

  4. Compare the result against the provided webhook-signature values.

Example in Node.js

Comparing Signatures

The webhook-signature header often looks like this:

v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE= v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=

Each signature is prefixed by a version identifier and a comma, such as v1,. To verify, remove the prefix (e.g., v1,) and compare the remaining Base64 string to your computed signature.

Use a constant-time comparison method to compare signatures and prevent timing attacks.

Verifying the Timestamp

The webhook-timestamp header includes the timestamp of the attempt. Make sure this timestamp is close to your current time (within a certain allowed skew, like a few minutes) to prevent replay attacks.

Example Signatures

Here is an example you can use to validate your manual verification logic. Note that this may fail verification due to an old timestamp, and that’s expected if you’re strictly enforcing freshness.

You can also use the open-source Standard Webhooks simulation tool to generate as many examples as needed.