Verifying Webhook Signatures from Trustpair

To ensure webhook requests are genuine and haven’t been tampered with, Trustpair signs all webhook payloads using HMAC-SHA256. We recommend all clients validate the signature before processing the request.

🧾 What’s included in each webhook request?

Each webhook request includes two custom HTTP headers:

  • x-trustpair-timestamp: The UNIX timestamp at which the request was sent.
  • x-trustpair-signature: A signature string in the format sha256=... used to verify the payload.

✅ How Signature Verification Works

Step-by-step:

  1. Concatenate the timestamp and the raw request body with a colon: signature_data = "{X-Trustpair-Timestamp}:{raw_body}"
  2. Hash this string using HMAC with the SHA-256 algorithm and with your client secret as the key. Your client secret is unique per webhook and can be obtained from Customer Support upon request.
  3. Compare the resulting hash (prefixed with sha256=) with the value from the X-Trustpair-Signature header.

💡 You can test this manually using this online HMAC-SHA256 tool. Use your client secret as the key and the {timestamp}:{raw_payload} string as the message.


🧪 Code Examples

Below are working examples in various languages:

const crypto = require('crypto');

const client_secret = "57s4h6cxduCs9GO9";

const request = {
  headers: {
    "X-Trustpair-Signature": "sha256=58de2260d2be3a130834a329ac684d7c895cc276b8a76fbb2b5d5e0597b4b854",
    "X-Trustpair-Timestamp": "1691760849"
  },
  body: "raw-request-body"
};

class TrustpairSignatureCheckService {
  constructor(client_secret, request) {
    this.client_secret = client_secret;
    this.request = request;
  }

  valid_signature() {
    return this.computed_signature() === this.request_signature();
  }

  computed_signature() {
    const signature_data = `${this.request_timestamp()}:${this.request_body()}`;
    const digest = crypto.createHmac('sha256', this.client_secret).update(signature_data).digest('hex');
    return `sha256=${digest}`;
  }

  request_timestamp() {
    return this.request.headers["X-Trustpair-Timestamp"];
  }

  request_body() {
    return this.request.body;
  }

  request_signature() {
    return this.request.headers["X-Trustpair-Signature"];
  }
}

const trustpairService = new TrustpairSignatureCheckService(customer_secret, request);
console.log(trustpairService.valid_signature());

client_secret = "57s4h6cxduCs9GO9"

request = OpenStruct.new({
  "headers" => {
    "X-Trustpair-Signature" => "sha256=58de2260d2be3a130834a329ac684d7c895cc276b8a76fbb2b5d5e0597b4b854",
    "X-Trustpair-Timestamp" => "1691760849"
  },
  "body" => "raw-request-body"
})

class TrustpairSignatureCheckService
  attr_reader :client_secret, :request

  def initialize(client_scret, request)
    @client_secret = client_secret
    @request = request
  end

  def valid_signature?
    computed_signature == request_signature
  end

private

  def computed_signature
    signature_data = "#{request_timestamp}:#{request_body}"
    digest = OpenSSL::HMAC.hexdigest('SHA256', client_secret, signature_data)
    "sha256=#{digest}"
  end

  def request_timestamp
    request.headers["X-Trustpair-Timestamp"]
  end

  def request_body
    request.body
  end

  def request_signature
    request.headers["X-Trustpair-Signature"]
  end
end

TrustpairSignatureCheckService.new(client_secret, request).valid_signature?

import hashlib
from collections import namedtuple

class OpenStruct:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

client_secret = "57s4h6cxduCs9GO9"

request = OpenStruct(
    headers = {
        "X-Trustpair-Signature": "sha256=58de2260d2be3a130834a329ac684d7c895cc276b8a76fbb2b5d5e0597b4b854",
        "X-Trustpair-Timestamp": "1691760849"
    },
    body = "raw-request-body"
)

class TrustpairSignatureCheckService:
    def __init__(self, client_secret, request):
        self.client_secret = client_secret
        self.request = request
    
    def valid_signature(self):
        return self.computed_signature() == self.request_signature()
    
    def computed_signature(self):
        signature_data = f"{self.request_timestamp()}:{self.request_body()}"
        digest = hashlib.sha256(self.client_secret.encode() + signature_data.encode()).hexdigest()
        return f"sha256={digest}"
    
    def request_timestamp(self):
        return self.request.headers["X-Trustpair-Timestamp"]
    
    def request_body(self):
        return self.request.body
    
    def request_signature(self):
        return self.request.headers["X-Trustpair-Signature"]

trustpair_service = TrustpairSignatureCheckService(client_secret, request)
print(trustpair_service.valid_signature())
class OpenStruct {
    private $data = array();

    public function __construct($data) {
        $this->data = $data;
    }

    public function __get($name) {
        return $this->data[$name];
    }
}

$client_secret = "57s4h6cxduCs9GO9";

$request = new OpenStruct(array(
    "headers" => array(
        "X-Trustpair-Signature" => "sha256=58de2260d2be3a130834a329ac684d7c895cc276b8a76fbb2b5d5e0597b4b854",
        "X-Trustpair-Timestamp" => "1691760849"
    ),
    "body" => "raw-request-body"
));

class TrustpairSignatureCheckService {
    private $client_secret;
    private $request;

    public function __construct($client_secret, $request) {
        $this->client_secret = $client_secret;
        $this->request = $request;
    }

    public function valid_signature() {
        return $this->computed_signature() === $this->request_signature();
    }

    private function computed_signature() {
        $signature_data = $this->request_timestamp() . ":" . $this->request_body();
        $digest = hash_hmac('sha256', $signature_data, $this->client_secret);
        return "sha256=" . $digest;
    }

    private function request_timestamp() {
        return $this->request->headers["X-Trustpair-Timestamp"];
    }

    private function request_body() {
        return $this->request->body;
    }

    private function request_signature() {
        return $this->request->headers["X-Trustpair-Signature"];
    }
}

$trustpair_service = new TrustpairSignatureCheckService($client_secret, $request);
echo $trustpair_service->valid_signature() ? "true" : "false";
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

type OpenStruct struct {
	data map[string]interface{}
}

func NewOpenStruct(data map[string]interface{}) *OpenStruct {
	return &OpenStruct{data: data}
}

func (o *OpenStruct) Get(key string) interface{} {
	return o.data[key]
}

func main() {
	clientSecret := "57s4h6cxduCs9GO9"

	request := NewOpenStruct(map[string]interface{}{
		"headers": map[string]string{
			"X-Trustpair-Signature": "sha256=58de2260d2be3a130834a329ac684d7c895cc276b8a76fbb2b5d5e0597b4b854",
			"X-Trustpair-Timestamp": "1691760849",
		},
		"body": "raw-request-body",
	})

	type TrustpairSignatureCheckService struct {
		clientSecret string
		request  *OpenStruct
	}

	func NewTrustpairSignatureCheckService(clientSecret string, request *OpenStruct) *TrustpairSignatureCheckService {
		return &TrustpairSignatureCheckService{
			clientSecret: clientSecret,
			request:  request,
		}
	}

	func (t *TrustpairSignatureCheckService) ValidSignature() bool {
		return t.computedSignature() == t.requestSignature()
	}

	func (t *TrustpairSignatureCheckService) computedSignature() string {
		signatureData := fmt.Sprintf("%s:%s", t.requestTimestamp(), t.requestBody())
		h := hmac.New(sha256.New, []byte(t.clientSecret))
		h.Write([]byte(signatureData))
		digest := hex.EncodeToString(h.Sum(nil))
		return fmt.Sprintf("sha256=%s", digest)
	}

	func (t *TrustpairSignatureCheckService) requestTimestamp() string {
		return t.request.Get("headers").(map[string]string)["X-Trustpair-Timestamp"]
	}

	func (t *TrustpairSignatureCheckService) requestBody() string {
		return t.request.Get("body").
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Map;
import java.util.HashMap;

class OpenStruct {
    private Map<String, Object> data = new HashMap<>();

    public OpenStruct(Map<String, Object> data) {
        this.data = data;
    }

    public Object get(String key) {
        return data.get(key);
    }
}

public class TrustpairSignatureCheckService {
    private String clientSecret;
    private OpenStruct request;

    public TrustpairSignatureCheckService(String clientSecret, OpenStruct request) {
        this.clientSecret = clientSecret;
        this.request = request;
    }

    public boolean validSignature() {
        return computedSignature().equals(requestSignature());
    }

    private String computedSignature() throws Exception {
        String signatureData = requestTimestamp() + ":" + requestBody();
        Mac hmacSha256 = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(clientSecret.getBytes(), "HmacSHA256");
        hmacSha256.init(secretKey);
        byte[] digest = hmacSha256.doFinal(signatureData.getBytes());
        return "sha256=" + bytesToHex(digest);
    }

    private String requestTimestamp() {
        return (String) request.get("headers").get("X-Trustpair-Timestamp");
    }

    private String requestBody() {
        return (String) request.get("body");
    }

    private String requestSignature() {
        return (String) request.get("headers").get("X-Trustpair-Signature");
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        }
        return result.toString();
    }

    public static void main(String[] args) {
        String clientSecret = "57s4h6cxduCs9GO9";

        Map<String, Object> headers = new HashMap<>();
        headers.put("X-Trustpair-Signature", "sha256=58de2260d2be3a130834a329ac684d7c895cc276b8a76fbb2b5d5e0597b4b854");
        headers.put("X-Trustpair-Timestamp", "1691760849");

        Map<String, Object> requestData = new HashMap<>();
        requestData.put("headers", headers);
        requestData.put("body", "raw-request-body");

        OpenStruct request = new OpenStruct(requestData);

        TrustpairSignatureCheckService trustpairService = new TrustpairSignatureCheckService(clientSecret, request);
        System.out.println(trustpairService.validSignature());
    }
}