Securing API Webhooks

Trackops signs all webhooks so you can verify they are genuinely from us and have not been tampered with.

Why Signature Verification Matters

Signature verification protects you from:

  • Spoofed requests pretending to be from Trackops
  • Tampered payload data
  • Replay attacks (someone resending old webhooks)

Headers We Send

Every webhook includes these three additional headers:

Header Description
X-Trackops-Signature HMAC-SHA256 signature of the request
X-Trackops-Timestamp Unix timestamp (seconds) when the webhook was sent
X-Trackops-Token Unique random token for this webhook

How to Verify the Webhook

Step 1: Obtain your Signing Key 

When you create a webhook in your API token settings, the system will generate a new webhook signing key. Signing keys are unique per webhook.  Locate your API webhook and securely obtain the webhook signing key.  Webhook signing keys should be treated like passwords and stored accordingly.

Step 2: Verify the Signature using HMAC

HMAC (Hash-based Message Authentication Code) is a secure way to verify that a webhook really came from Trackops and that the data has not been altered. This process guarantees:

  • The request came from Trackops (only we know the signing key)
  • The payload wasn't changed in transit
  • Combined with the timestamp and token, it also helps prevent replay attacks

Use functions in your desired programming language to generate an SHA256 HMAC signature and compare the generated output to the Trackops signature header.  If the signature matches exactly, the webhook is valid and can be trusted.

Node.js Example

const crypto = require('crypto');

const verify = (rawBody, headers, signingKey) => {
    const sig = headers['x-trackops-signature'];
    const ts = headers['x-trackops-timestamp'];
    const token = headers['x-trackops-token'];

    const data = ts + ':' + token + ':' + rawBody;
    const expected = crypto.createHmac('sha256', signingKey)
                           .update(data)
                           .digest('hex');

    return expected === sig;
};

PHP Example

function verifyWebhook($rawBody, $headers, $signingKey) {
    $sig   = $headers['X-Trackops-Signature'] ?? '';
    $ts    = $headers['X-Trackops-Timestamp'] ?? '';
    $token = $headers['X-Trackops-Token'] ?? '';

    $data = $ts . ':' . $token . ':' . $rawBody;
    $expected = hash_hmac('sha256', $data, $signingKey);

    return hash_equals($expected, $sig);
}

Python Example

import hmac
import hashlib

def verify_webhook(raw_body, headers, signing_key):
    sig = headers.get('X-Trackops-Signature')
    ts = headers.get('X-Trackops-Timestamp')
    token = headers.get('X-Trackops-Token')

    data = f"{ts}:{token}:{raw_body.decode('utf-8')}"
    expected = hmac.new(
        key=signing_key.encode(),
        msg=data.encode(),
        digestmod=hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, sig)

 

Was this article helpful?
0 out of 0 found this helpful
Have more questions? Submit a request