HMAC Authentication
Private API endpoints require authentication using HMAC SHA256 signatures generated with your API Secret.
The authentication ensures:
- Request integrity
- Request authenticity
- Protection against replay attacks
Authentication Flow
Every authenticated request follows this process:
- Generate a timestamp
- Build the payload string
- Generate HMAC SHA256 signature
- Base64 encode the signature
- Send request with required headers
- Server validates signature
Required Headers
All private endpoints require these headers:
| Header | Required | Description |
|---|---|---|
X-API-Key | Yes | Your cabinet generated API key |
X-Signature | Yes | Base64 encoded HMAC SHA256 signature |
X-Timestamp | Yes | Request timestamp in milliseconds |
X-Recv-Window | No | Maximum allowed request age in ms |
Step 1 — Generate Timestamp
Timestamp must be UNIX time in milliseconds.
Example:
1770990729000
Example (Javascript):
const timestamp = Date.now().toString()
Step 2 — Build Payload
Payload format:
HTTP_METHOD + "\n" +
PATH + "\n" +
TIMESTAMP + "\n" +
RECV_WINDOW + "\n" +
BODY
Important rules:
- Method must be uppercase
- Path must include query parameters
- New lines must be preserved
- Body must match request exactly
- Empty body must be empty string
Payload Examples
GET example
Request:
GET /open_api/api_profiles?exchanges=BINANCE,KRAKEN
Payload:
GET
/open_api/api_profiles?exchanges=BINANCE,KRAKEN
1770990729000
60000
GET requests have empty body.
POST example
Request:
POST /open_api/position
Body:
{
"key":"value",
"key1":"value1"
}
Payload:
POST
/open_api/position
1770990729000
60000
{"key":"value","key1":"value1"}
Body must match serialized JSON exactly.
Step 3 — Generate Signature
Signature formula:
Base64(HMAC_SHA256(Secret, Payload))
Step 4 — Add Headers
Example headers:
X-API-Key: your_api_key
X-Signature: generated_signature
X-Timestamp: 1770990729000
X-Recv-Window: 60000
Implementation Examples
- Postman
- JavaScript
- Python
// Pre-request Script
const apiKey = "your_api_key";
const apiSecret = "your_secret_key";
const timestamp = Date.now().toString();
const recvWindow = 60000;
const method = pm.request.method;
let path = "/" + pm.request.url.path.join("/");
if (pm.request.url.query.count() > 0) {
path += "?" + pm.request.url.query.map((q) => { return `${q.key}=${q.value}` }).join("&");
}
let body = "";
if (pm.request.body && pm.request.body.raw) {
body = pm.request.body.raw;
}
const payload =
method + "\n" +
path + "\n" +
timestamp + "\n" +
(recvWindow ?? '') + "\n" +
body;
const signature = CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(payload, apiSecret)
);
pm.request.headers.upsert({
key: "X-API-Key",
value: apiKey
});
pm.request.headers.upsert({
key: "X-Timestamp",
value: timestamp
});
pm.request.headers.upsert({
key: "X-Signature",
value: signature
});
if (recvWindow) {
pm.request.headers.upsert({
key: "X-Recv-Window",
value: recvWindow
});
}
const crypto = require("crypto")
const method = "GET"
const path = "/open_api/api_profiles?exchanges=BINANCE,KRAKEN"
const timestamp = Date.now().toString()
const recvWindow = "60000"
const body = ""
const payload = [
method,
path,
timestamp,
recvWindow,
body
].join("\n")
const signature = crypto
.createHmac("sha256", API_SECRET)
.update(payload)
.digest("base64")
console.log(signature)
import hmac
import hashlib
import base64
import time
method = "GET"
path = "/open_api/api_profiles?exchanges=BINANCE,KRAKEN"
timestamp = str(int(time.time() * 1000))
recv_window = "60000"
body = ""
payload = "\n".join([
method,
path,
timestamp,
recv_window,
body
])
signature = base64.b64encode(
hmac.new(
API_SECRET.encode(),
payload.encode(),
hashlib.sha256
).digest()
).decode()
print(signature)
Server Validation Process
Server validates:
- API key exists and not expired
- Signature matches
- Timestamp is valid
- Request is within receive window
- Payload integrity
Validation logic example:
abs(server_time - timestamp) <= recv_window
If X-Recv-Window is not provided:
Default window:
10000 ms
Security Requirements
Never share your API Secret.
Never send API Secret in requests.
Always generate signature server-side.
Rotate keys if compromised.
Common Mistakes
Most authentication failures are caused by:
| Problem | Cause |
|---|---|
| Invalid signature | Wrong payload formatting |
| Invalid signature | Missing newline characters |
| Invalid signature | Body mismatch |
| Timestamp error | Client clock incorrect |
| Expired request | Receive window too small |
| Invalid signature | Method not uppercase |
Best Practices
Recommended practices:
- Sync time using NTP
- Use 30–60 second receive window
- Always stringify JSON consistently
- Log payload during debugging
- Never modify body after signing
Summary
Authentication requires:
Signature = Base64(HMAC_SHA256(secret, payload))
Payload:
method + path + timestamp + recv_window + body
Headers:
X-API-Key
X-Signature
X-Timestamp
X-Recv-Window