Quick Start Guide
Welcome to the Kalqix API integration guide. This document will help you authenticate, access market data, manage orders, and monitor your account.
Table of Contents
- Getting Started
- Authentication
- HMAC Signature Generation
- Ethereum Message Signature
- Complete Order Example
Getting Started
Web App
Get your API key and secret from the Kalqix web app:
- Navigate to User > Settings > API Keys
- Click "Create New API Key"
- Save your
api_keyandapi_secretsecurely
Testnet Web App: https://testnet.kalqix.com
API Base URL
All API requests should be made to:
https://testnet-api.kalqix.com/v1/
Example endpoints:
- Orders:
https://testnet-api.kalqix.com/v1/orders - Withdrawals:
https://testnet-api.kalqix.com/v1/withdrawals - Market Data:
https://testnet-api.kalqix.com/v1/markets
Authentication
Required Headers
Every API request must include these headers:
const headers = {
'x-api-key': 'your-api-key',
'x-api-signature': 'hmac-signature',
'x-api-timestamp': 'timestamp-in-ms',
'Content-Type': 'application/json'
};
Header Descriptions:
x-api-key(string): Your API key from Settingsx-api-signature(string): HMAC-SHA256 signature of the request (see below)x-api-timestamp(string): Current Unix timestamp in millisecondsContent-Type(string): Always "application/json"
HMAC Signature Generation
We use HMAC-SHA256 to authenticate API requests. This allows the server to verify that a request was created by someone who holds your API secret without ever sending the secret over the network.
How It Works
- Create a canonical string from the request details
- Sign it with your API secret using HMAC-SHA256
- Send the signature in the
x-api-signatureheader
Canonicalization Helper
For consistency, use this helper function to canonicalize JSON payloads:
function canonicalizePayload(payload) {
return JSON.stringify(payload, Object.keys(payload).sort());
}
Implementation
const crypto = require('crypto');
function signRequest(method, path, body, timestamp, apiSecret) {
let payload = '';
if (body && Object.keys(body).length > 0) {
payload = canonicalizePayload(body);
}
const canonical = `${method}|${path}|${payload}|${timestamp}`;
return crypto.createHmac('sha256', apiSecret)
.update(canonical)
.digest('hex');
}
Example Usage
const method = 'POST';
const path = '/v1/orders';
const body = {
ticker: 'BTC_USDC',
price: '100000',
quantity: '0.1',
side: 'BUY',
order_type: 'LIMIT',
timestamp: Date.now()
};
const timestamp = Date.now();
const signature = signRequest(method, path, body, timestamp, apiSecret);
Important Notes:
- The timestamp must match the one sent in the
x-api-timestampheader - The path must include the
/v1/prefix - Request body is canonicalized with sorted keys for consistency
- For requests without a body, the payload will be an empty string
- Signatures expire after 5 minutes to prevent replay attacks
Ethereum Message Signature
For Order Placement
For order placement, you also need to sign a message with your Ethereum wallet. This signature proves ownership of the trading account.
Important: This signature goes in the request body, not the headers (unlike the HMAC signature).
Message Signing Requirements (v1.4.0+)
All signed messages must:
- Include an explicit
actionfield (e.g.,PLACE_ORDER,CANCEL_ORDER,TRANSFER,WITHDRAW) - Include a
timestampfield as integer milliseconds (useDate.now()) - Use canonicalized JSON with sorted keys for consistency
- Be within 5 minutes of server time (replay protection)
Implementation
const { ethers } = require('ethers');
async function signOrderMessage(order, walletSeed) {
// 1. Create wallet from seed phrase
const wallet = ethers.Wallet.fromPhrase(walletSeed);
// 2. Build the signed payload with action and timestamp
const payload = {
action: 'PLACE_ORDER',
ticker: order.ticker,
price: order.price,
quantity: order.quantity,
side: order.side,
order_type: order.order_type,
timestamp: Date.now()
};
// 3. Canonicalize and sign
const signingString = canonicalizePayload(payload);
const signature = await wallet.signMessage(signingString);
const walletAddress = wallet.address;
return {
signature,
walletAddress,
timestamp: payload.timestamp
};
}
Example Usage
const order = {
ticker: 'BTC_USDC',
price: '100000',
quantity: '0.1',
side: 'BUY',
order_type: 'LIMIT'
};
// Your wallet seed phrase (12, 15, 18, 21, or 24 words)
const walletSeed = 'your twelve word seed phrase here for wallet creation';
const { signature, walletAddress, timestamp } = await signOrderMessage(order, walletSeed);
// Output: '{"action":"PLACE_ORDER","order_type":"LIMIT","price":"100000","quantity":"0.1","side":"BUY","ticker":"BTC_USDC","timestamp":1767225600000}'
console.log('Signature:', signature);
// Output: "0x..."
console.log('Wallet Address:', walletAddress);
// Output: "0x..."
console.log('Timestamp:', timestamp);
// Output: 1767225600000
Important Notes:
- Timestamps must be integers in milliseconds (use
Date.now()) - Messages expire after 5 minutes for security
- Use
canonicalizePayload()to ensure consistent key ordering - The
actionfield is required for all signed operations - The signed payload includes
action, but the API request body does not
Alternative Wallet Creation Methods
You can create wallets using different methods depending on your security setup:
// Method 1: From private key
const privateKey = '0x...'; // Your private key
const wallet = new ethers.Wallet(privateKey);
// Method 2: From mnemonic/seed phrase (recommended for user accounts)
const mnemonic = 'your twelve word mnemonic here';
const wallet = ethers.Wallet.fromPhrase(mnemonic);
// Method 3: Generate a new random wallet (for testing)
const randomWallet = ethers.Wallet.createRandom();
console.log('New wallet address:', randomWallet.address);
console.log('New wallet mnemonic:', randomWallet.mnemonic.phrase);
Security Best Practices:
- Never hardcode private keys or seed phrases in your code
- Use environment variables for sensitive credentials
- Consider using a hardware wallet for production systems
- For trading bots, use dedicated API-only wallets with limited funds
Complete Order Placement Example
This example demonstrates the complete flow of placing an order, combining both HMAC authentication and wallet signature.
/**
* Complete Example: Programmatically place an order on Kalqix
*
* This script:
* - Signs an order using your wallet (mnemonic or private key)
* - Signs the HTTP request using your API secret (HMAC)
* - Sends a POST /v1/orders request to the Kalqix API
*
* Required environment variables:
* - API_KEY
* - API_SECRET
* - WALLET_SEED (mnemonic phrase) OR
* - WALLET_PRIVATE_KEY
*/
require('dotenv').config();
const crypto = require('crypto');
const { ethers } = require('ethers');
/**
* Configuration from environment
*/
const API_KEY = process.env.API_KEY;
const API_SECRET = process.env.API_SECRET;
// Set either WALLET_SEED or WALLET_PRIVATE_KEY
const WALLET_SEED = process.env.WALLET_SEED;
const WALLET_PRIVATE_KEY = process.env.WALLET_PRIVATE_KEY;
/**
* Canonicalize JSON payload (sorted keys)
*/
function canonicalizePayload(payload) {
return JSON.stringify(payload, Object.keys(payload).sort());
}
/**
* Create an HMAC signature for a Kalqix API request.
*/
function signRequest(method, path, body, timestamp, apiSecret) {
let payload = '';
if (body && Object.keys(body).length > 0) {
payload = canonicalizePayload(body);
}
const canonical = `${method}|${path}|${payload}|${timestamp}`;
return crypto.createHmac('sha256', apiSecret).update(canonical).digest('hex');
}
/**
* Place an order with wallet and HMAC signatures
*/
async function placeOrder(orderData) {
let wallet;
if (WALLET_SEED) {
wallet = ethers.Wallet.fromPhrase(WALLET_SEED);
} else if (WALLET_PRIVATE_KEY) {
wallet = new ethers.Wallet(WALLET_PRIVATE_KEY);
} else {
throw new Error('Please provide WALLET_SEED or WALLET_PRIVATE_KEY in environment variables.');
}
const timestamp = Date.now();
// 1. Build the payload (without action)
const payload = {
ticker: orderData.ticker,
side: orderData.side,
order_type: orderData.order_type,
quantity: orderData.quantity,
quote_quantity: orderData.quote_quantity || '',
price: orderData.price || '',
time_in_force: orderData.time_in_force || 0,
expires_at: orderData.expires_at || 0,
timestamp
};
// 2. Sign with wallet (payload + action)
const signingString = canonicalizePayload({ action: 'PLACE_ORDER', ...payload });
const walletSignature = await wallet.signMessage(signingString);
// 3. Request body = payload + signature (no action field)
const requestBody = {
...payload,
signature: walletSignature
};
// 4. Generate HMAC signature for headers
const method = 'POST';
const path = '/v1/orders';
const hmacSignature = signRequest(method, path, requestBody, timestamp, API_SECRET);
// 5. Send request
const response = await fetch('https://testnet-api.kalqix.com/v1/orders', {
method: 'POST',
headers: {
'x-api-key': API_KEY,
'x-api-signature': hmacSignature, // HMAC for API auth
'x-api-timestamp': timestamp.toString(),
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody) // Contains Ethereum signature
});
return await response.json();
}
// Example usage
(async () => {
const result = await placeOrder({
ticker: 'BTC_USDC',
price: '100000',
quantity: '0.1',
side: 'BUY',
order_type: 'LIMIT'
});
console.log('Order result:', result);
})().catch(console.error);
Running the Example
-
Install dependencies:
npm install dotenv ethers -
Create a
.envfile:API_KEY=your_api_key_here
API_SECRET=your_api_secret_here
WALLET_SEED=your twelve word seed phrase here
# OR
WALLET_PRIVATE_KEY=0x... -
Run the script:
node place_order.js
Expected Response
Success (200):
{
"order_id": "abc123...",
"ticker": "BTC_USDC",
"price": "100000",
"quantity": "0.1",
"side": "BUY",
"order_type": "LIMIT",
"status": "PENDING",
"created_at": "2026-02-07T12:00:00.000Z"
}
Important Notes
Timestamp Requirements
- Timestamps must be integers in milliseconds (not ISO strings)
- Requests rejected if timestamp is in the future or older than 5 minutes (replay protection)
- Use
Date.now()in JavaScript orint(time.time() * 1000)in Python - All signed message payloads must include timestamp field