Verifying webhook signature

Securely verify that the webhook events are from Cake capital

When Cake Capital sends a webhook event to your server, it's crucial to verify that the payload was indeed sent by us and not tampered with. To ensure this, we sign the payload using a secret key, and you can use this document to verify the signature.

Example webhook payload

Here's an example of a webhook payload sent by Cake Capital:

{
    "id": "38e67b16-d477-43b9-921b-a40cebb3bf2a",
    "created_at": 1714062202544,
    "event_name": "transaction-created",
    "entity" :{
        "id": "32a603e8-fa60-4898-8b87-55b4caa202a1",
        "type": "transaction"
    },
    "resource_url": "https://sandbox.cakecapital.com/api/v1/send/transactions/32a603e8-fa60-4898-8b87-55b4caa202a1/"
}

Headers sent with the webhook event

Along with the payload, the following headers are sent:

X-Timestamp: The timestamp (in seconds from epoch) when the payload was created.
X-Signature: The signature used to verify the authenticity of the payload.

Signature generation process

The signature is generated using the following process:

  1. Concatenate the payload.id, the string --cake--, and the X-Timestamp.

    Example: 38e67b16-d477-43b9-921b-a40cebb3bf2a--cake--1714062202544

  2. Create an HMAC-SHA512 hash of this string using your secret key.
  3. The resulting hash is the signature, which you can compare against the X-Signature header to verify the payload.

Code examples for verifying the signature

const crypto = require('crypto');

function verifySignature(payload, secretKey, xTimestamp, xSignature) {
    const payloadString = `${payload.id}-cake-${xTimestamp}`;
    const hmac = crypto.createHmac('sha512', secretKey);
    hmac.update(payloadString);
    const generatedSignature = hmac.digest('hex');
    
    return generatedSignature === xSignature;
}

// Example usage
const payload = {
    id: "38e67b16-d477-43b9-921b-a40cebb3bf2a",
    created_at: 1714062202544,
    event_name: "transaction-created",
    entity: {
        id: "32a603e8-fa60-4898-8b87-55b4caa202a1",
        type: "transaction"
    },
    resource_url: "https://sandbox.cakecapital.com/api/v1/send/transactions/32a603e8-fa60-4898-8b87-55b4caa202a1/"
};

const secretKey = "ccws_10b851dc46a248bc8b0602d8941";
const xTimestamp = "1714062202544";
const xSignature = "the_signature_from_header";

const isValid = verifySignature(payload, secretKey, xTimestamp, xSignature);
console.log("Signature is valid:", isValid);
import hmac
import hashlib

def verify_signature(payload, secret_key, x_timestamp, x_signature):
    payload_string = f"{payload['id']}-cake-{x_timestamp}"
    payload_bytes = payload_string.encode('utf-8')
    secret_key_bytes = secret_key.encode('utf-8')
    
    generated_signature = hmac.new(secret_key_bytes, payload_bytes, digestmod=hashlib.sha512).hexdigest()
    
    return hmac.compare_digest(generated_signature, x_signature)

# Example usage
payload = {
    "id": "38e67b16-d477-43b9-921b-a40cebb3bf2a",
    "created_at": 1714062202544,
    "event_name": "transaction-created",
    "entity": {
        "id": "32a603e8-fa60-4898-8b87-55b4caa202a1",
        "type": "transaction"
    },
    "resource_url": "https://sandbox.cakecapital.com/api/v1/send/transactions/32a603e8-fa60-4898-8b87-55b4caa202a1/"
}

secret_key = "ccws_10b851dc46a248bc8b0602d8941"
x_timestamp = "1714062202544"
x_signature = "the_signature_from_header"

is_valid = verify_signature(payload, secret_key, x_timestamp, x_signature)
print("Signature is valid:", is_valid)