Articles on: Developers

Important Note

⚠️ Important: All Requests Must Be Signed


To ensure request authenticity and integrity, all requests sent from your backend to COUNT's API must be signed using your client secret. This signature helps prevent tampering, replay attacks, and unauthorized access.


We strongly recommend using a reusable Axios instance with request signing built in, as shown below.


🔐 Signature Requirements


Each request must include three critical headers:

  • x-client-id should contain your unique client identifier, which you’ll find in your COUNT dashboard.
  • x-timestamp must be the current UNIX timestamp in seconds when the request is made.
  • x-signature is the computed HMAC SHA-256 signature based on the request details and your client secret.


The signature is generated using a combination of the HTTP method, the request path (excluding domain), the timestamp, and the hashed request body (for POSTPUT, or PATCH methods). These are combined into a single string, hashed using your client secret, and attached to the request.


Below is the recommended setup using a reusable Axios client to handle signing automatically:


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

// Create an Axios instance with base URL and static x-client-id header

const countClient = axios.create({
baseURL: process.env.COUNT_PARTNER_API_ENDPOINT,
headers: {
"x-client-id": process.env.COUNT_CLIENT_ID,
},
});

// Attach the interceptor with a reusable signing function

countClient.interceptors.request.use(signRequest, (error) => Promise.reject(error));

// Function that handles signing the request

function signRequest(config) {
const method = (config.method || "GET").toUpperCase();
const url = config.url || "/";
const urlPath = new URL(url, config.baseURL).pathname;
const timestamp = Math.floor(Date.now() / 1000).toString();
const clientSecret = process.env.COUNT_CLIENT_SECRET;
const body = config.data || {}; const signature = generateSignature({
method,
path: urlPath,
timestamp,
body,
clientSecret,
}); config.headers["x-timestamp"] = timestamp;
config.headers["x-signature"] = signature; return config;
}

// Hash the request body using SHA-256

function hashBody(body) {
return body && Object.keys(body).length > 0
? crypto.createHash("sha256").update(JSON.stringify(body)).digest("hex")
: "";
}

// Build the HMAC base string in the format: METHOD:/path:timestamp:bodyHash

function buildHmacBaseString(method, path, timestamp, bodyHash = "") {
return ${method}:${path}:${timestamp}:${bodyHash};
}

// Generate the HMAC-SHA256 signature

function generateSignature({ method, path, timestamp, body, clientSecret }) {
const bodyHash = ["POST", "PUT", "PATCH"].includes(method)
? hashBody(body)
: ""; const baseString = buildHmacBaseString(method, path, timestamp, bodyHash); return crypto
.createHmac("sha256", clientSecret)
.update(baseString)
.digest("hex");
}


Ensure your server time is accurate and your clientSecret is never exposed on the client side. Requests with invalid or expired signatures will be rejected by COUNT’s API.



Updated on: 08/10/2025

Was this article helpful?

Share your feedback

Cancel

Thank you!