Signature Verification
Every webhook request from Terminal3 includes a signature so you can verify it is authentic and has not been tampered with. It is your responsibility to verify this signature before processing the request.
Header
Each webhook request contains the following header:
X-Webhook-Signature: t=1688000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
The header has two components, separated by a comma:
| Component | Description |
|---|---|
t |
Unix timestamp (in seconds) when the request was signed. |
v1 |
HMAC-SHA256 hex digest of the signing string. |
How to Verify
Step 1: Parse the header
Split the X-Webhook-Signature value by , and extract the t and v1 values.
Step 2: Build the signing string
Concatenate the timestamp t, a literal period ., and the raw request body (the JSON payload exactly as received, with no parsing or reformatting):
1688000000.{"order_id":123,"uid":"user123","status":"completed"}
Step 3: Compute the expected signature
Calculate the HMAC-SHA256 hex digest of the signing string using your Signing Secret (the whsec_ prefixed key found in your Webhook Endpoint Configuration):
expected = hex(hmac_sha256(signing_string, endpoint_secret))
Step 4: Compare signatures
Compare your computed signature with the v1 value from the header. Use a constant-time comparison function (e.g. hash_equals in PHP, hmac.compare_digest in Python) to prevent timing attacks.
Step 5: Check timestamp (recommended)
Compare t against the current time. Reject the request if the timestamp is too far in the past (e.g. more than 5 minutes) to protect against replay attacks.
Example
Given:
- Signing Secret:
whsec_test123 - Raw body:
{"order_id":123,"uid":"user123"} - Header:
X-Webhook-Signature: t=1688000000,v1=abc123...
Verification:
signing_string = "1688000000.{\"order_id\":123,\"uid\":\"user123\"}"
expected = hex(hmac_sha256(signing_string, "whsec_test123"))
verified = constant_time_equal(expected, "abc123...")