Authentication
Every request to the Light Horse API must include both an API key and a cryptographic signature. The signature is computed from the full request content — method, path, query string, headers, and body — so any tampering in transit will cause the signature check to fail and the request to be rejected.
Required Headers
Every request must include the following HTTP headers:
x-trade-apikey
A unique ID issued when your API key pair is created. This identifies the key to use for cryptographic signature.
x-trade-algorithm
The algorithm used to generate the signature. Currently only HMAC-SHA256 is supported.
x-trade-nonce
Any arbitrary unique value, for example an UUID. The server rejects any request that reuses a nonce it has already seen.
Example: d3a6c7b1-8e4f-4a2d-9c3b-1f8e7d6c5b4a
x-trade-timestamp
Unix timestamp (seconds) at the time of the request. Requests with a timestamp older than 5 minutes are rejected.
Example: 1765148421, which corresponds to 2025-12-07 23:00:21
x-trade-signature
The computed signature (see below).
Computing the Signature
Step 1 — Prepare the request body
- Serialize the body as json, if the request has no body, use empty JSON object string
{}. - Compute the MD5 hash of the resulting string.
Step 2 — Build the string to sign
Concatenate the following fields in order, separated by \n:
METHOD\n
REQUEST_PATH\n
QUERY_STRING\n
x-trade-apikey:X-Trade-APIKey\n
x-trade-timestamp:X-Trade-Timestamp\n
x-trade-nonce:X-Trade-Nonce\n
MD5(body)For example, a POST to /request/url?param1=value1¶m2=value2 with empty body produces:
POST\n
/request/url\n
param1=value1¶m2=value2\n
x-trade-apikey:739c38fa-0135-494d-88e1-f51e0ecc579c\n
x-trade-timestamp:1705148421\n
x-trade-nonce:d3a6c7b1-8e4f-4a2d-9c3b-1f8e7d6c5b4a\n
99914b932bd37a50b983c5e7c90ae93bStep 3 — Sign and encode
Compute HMAC-SHA256 over the string to sign using your API secret, then Base64-encode the result:
const bodyHash = crypto
.createHash('md5')
.update(JSON.stringify(sortedBody))
.digest('hex');
const stringToSign = [
method.toUpperCase(),
requestPath,
queryString,
apiKey,
timestamp,
nonce,
bodyHash,
].join('\n');
let signature = crypto
.createHmac('sha256', apiSecret)
.update(stringToSign)
.digest('hex');
signature = Buffer.from(signature).toString('base64');Set the resulting value as the x-trade-signature header.
Complete Example
import crypto from 'crypto';
function getSignedHeaders({ method, path, query, body, apiKey, apiSecret }) {
const nonce = crypto.randomUUID();
const timestamp = Math.floor(Date.now() / 1000).toString();
const sortedBody = body || '{}';
const bodyHash = crypto.createHash('md5').update(sortedBody).digest('hex');
const stringToSign = [
method.toUpperCase(),
path,
query ?? '',
`x-trade-apikey:${apiKey}`,
`x-trade-timestamp:${timestamp}`,
`x-trade-nonce:${nonce}`,
bodyHash
].join('\n');
let signature = crypto.createHmac('sha256', apiSecret).update(stringToSign).digest('hex');
signature = Buffer.from(signature).toString('base64');
return {
'x-trade-apikey': apiKey,
'x-trade-algorithm': 'HMAC-SHA256',
'x-trade-nonce': nonce,
'x-trade-timestamp': timestamp,
'x-trade-signature': signature,
};
}