Skip to main content

Authentication

This API uses an HMAC‑SHA256 signature derived from the request path and the SHA256 digest of the request body. The final signature is folded multiple times and sent in the Authorization header together with your API account key.


Required Headers

X-Api-Key: <your API account key>
Authorization: HMAC <base64_signature>
Accept: application/json

What Gets Signed

The following steps show exactly how the signature is calculated. You can verify your implementation by reproducing the example below, which signs a request to create a supplier.

For this worked example:

  • API Secret: d197b7819d6f914677270f939a4c67ad9dc4bd44076e6a0ca7bafab9235a7126
  • Folds: 5

Follow along to ensure your implementation produces the same output.


Step 1 - Canonical Path

Use the exact request path, including the leading slash and excluding any query parameters.

Example:

/api/public/v1/scorecards

Step 2 - Body Digest

Compute the SHA256 hash of the raw request body bytes, then encode the result as a lowercase hex string.

If the request has no body (e.g., GET), compute the SHA256 of the empty string "".

Example request body:

{"scorecard": { "description": "YTD Scorecard Nov 2024", "start_date": "2024-01-01", "end_date": "2024-11-30", "charter_id": "bravo_generic", "province": "National"}}

SHA256 digest:

726a4d0e2707c29beda838e4d0c8cca5753486c3057cf5a722abf65e8f4b3af1

Step 3 - Build the String to Sign

Concatenate the canonical path and the body digest with no delimiter.

Example:

/api/public/v1/scorecards726a4d0e2707c29beda838e4d0c8cca5753486c3057cf5a722abf65e8f4b3af1

Step 4 - Fold the Digest (Repeated HMAC)

Perform the following operation five times (or your configured fold count):

stringToSign[i+1] = HMAC_SHA256_HEX(secret, stringToSign[i])

Each fold uses the hex output of the previous fold as its input.


Step 5 - Base64 Encode the Final Fold

Take the hex output of the final fold and Base64‑encode it:

ODNjMzY5N2JmNDI4NWFkZjMwNzlhOTJiMTdmOTVjZGJkMzk0MzM4OGZiYTE5OTEyMWVlOWZjOTZkNmEzNTQ4Mg==

This is the final signature.


Step 6 - Build the Authorization Header

Insert the signature into the header exactly as follows:

Authorization: HMAC ODNjMzY5N2JmNDI4NWFkZjMwNzlhOTJiMTdmOTVjZGJkMzk0MzM4OGZiYTE5OTEyMWVlOWZjOTZkNmEzNTQ4Mg==

Your request is now fully authenticated and ready to send.

Example Code (NodeJS)

const crypto = require("crypto");
const axios = require("axios");

async function generateSignature(path, payload, secretKey, folds) {
const payloadString = payload ? JSON.stringify(payload) : "";
const payloadHash = crypto
.createHash("sha256")
.update(payloadString)
.digest("hex");

const stringToSign = `${path}${payloadHash}`;

let signature = crypto
.createHmac("sha256", secretKey)
.update(stringToSign)
.digest("hex");


for (let i = 1; i < folds; i++) {
signature = crypto
.createHmac("sha256", secretKey)
.update(signature)
.digest("hex");
}

return Buffer.from(signature).toString("base64");
}


async function createScorecard() {
const API_SECRET_KEY = process.env.API_SECRET_KEY;
const API_SIGNATURE_FOLDS = parseInt(process.env.API_SIGNATURE_FOLDS, 10);
const API_ACCOUNT_KEY = process.env.API_ACCOUNT_KEY;

const url = "https://www.beetoolkit.co.za/api/public/v1/scorecards";
const payload = {
scorecard: {
description: 'YTD Scorecard Nov 2024',
start_date: '2024-01-01',
end_date: '2024-11-30',
charter_id: 'bravo_generic',
province: 'National'
}
}
const path = new URL(url).pathname;
const base64Signature = await generateSignature(path, payload, API_SECRET_KEY, API_SIGNATURE_FOLDS);

try {
const response = await axios.post(
url,
payload,
{
headers: {
Accept: "application/json",
Authorization: `HMAC ${base64Signature}`,
'X-Api-Key': API_ACCOUNT_KEY,
},
}
);

console.log("Response:", response.data);
} catch (error) {
console.error("Error:", error.message);
}
}

createScorecard();