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 key>
Authorization: HMAC <base64_signature>
Accept: application/json

About API Keys

Each BEEtoolkit account can hold up to 5 API keys at any one time. Every key has an mpk_ prefix, a matching secret, and an optional expiry date.

  • Multiple active keys let you rotate without downtime: create the replacement, switch your integration over, then revoke the old key.
  • Expiries are optional. You can choose 30d, 90d, 180d, or 365d at creation time, or omit the parameter for a non-expiring key.
  • The secret is returned only once — at the moment of creation. Store it securely; it cannot be retrieved later.

Keys can be managed through the BEEtoolkit web UI or programmatically through the API. See API Keys for the create and revoke endpoints.


Key Expiry Headers

When you authenticate with a key that has an expiry set, every response includes:

HeaderWhen presentDescription
X-Api-Key-ExpiresAlways, if the key has an expiryISO 8601 timestamp of the expiry.
X-Api-Key-Expires-InOnly when 30 days or fewer remainRelative time left: Nd for days, Nh for hours in the final day.

Monitor these headers to rotate keys before they expire. Requests made with an expired key will be rejected with 401 Unauthorized.


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 scorecard.

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();