Check your implementation against this known-good result before going live. The signature is the HMAC-SHA256 of the canonical form, not of the bytes you receive.
Sign key
test_sign_key_0123456789abcdefPayload as received
{
"id": "01a10000-0000-7000-8000-000000000001",
"reference": "PAYOUT-2026-000123",
"paymentStatus": "COMPLETED",
"paymentType": "WITHDRAWAL",
"paymentMethod": "CARD",
"amount": 50,
"currency": "EUR",
"created": {
"date": "2026-10-06 12:34:56.000000",
"timezone_type": 3,
"timezone": "Europe/Vilnius"
},
"customer": {
"firstName": "JOHN",
"lastName": "DOE",
"email": null,
"phone": null,
"ipaddress": null,
"dateOfBirth": null,
"referenceId": "PLAYER-10045",
"accountNumber": null,
"bankCode": null
},
"providerId": "01a10000-0000-7000-8000-000000000001",
"fallbackPaymentStatus": "default"
}Canonical form — this exact string is what gets hashed
{
"id": "01a10000-0000-7000-8000-000000000001",
"reference": "PAYOUT-2026-000123",
"paymentStatus": "COMPLETED",
"paymentType": "WITHDRAWAL",
"paymentMethod": "CARD",
"amount": 50,
"currency": "EUR",
"created": {
"date": "2026-10-06 12:34:56.000000",
"timezone_type": 3,
"timezone": "Europe/Vilnius"
},
"customer": {
"firstName": "JOHN",
"lastName": "DOE",
"email": null,
"phone": null,
"ipaddress": null,
"dateOfBirth": null,
"referenceId": "PLAYER-10045",
"accountNumber": null,
"bankCode": null
},
"providerId": "01a10000-0000-7000-8000-000000000001",
"fallbackPaymentStatus": "default"
}Expected Signature header
4bc50699f81c526281d40c14a9d64a72ef3575960cc24c20ab7059243f048b6aIf your canonical string matches byte for byte but the signature differs, the problem is the key or the HMAC call. If the signature differs and the canonical string does too, compare them line by line — indentation, escaping and whole-number formatting are the usual culprits.