Skip to main content

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

  1. Getting Started
  2. Authentication
  3. HMAC Signature Generation
  4. Ethereum Message Signature
  5. Complete Order Example

Getting Started

Web App

Get your API key and secret from the Kalqix web app:

  1. Navigate to User > Settings > API Keys
  2. Click "Create New API Key"
  3. Save your api_key and api_secret securely

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 Settings
  • x-api-signature (string): HMAC-SHA256 signature of the request (see below)
  • x-api-timestamp (string): Current Unix timestamp in milliseconds
  • Content-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

  1. Create a canonical string from the request details
  2. Sign it with your API secret using HMAC-SHA256
  3. Send the signature in the x-api-signature header

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-timestamp header
  • 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:

  1. Include an explicit action field (e.g., PLACE_ORDER, CANCEL_ORDER, TRANSFER, WITHDRAW)
  2. Include a timestamp field as integer milliseconds (use Date.now())
  3. Use canonicalized JSON with sorted keys for consistency
  4. 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 action field 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

  1. Install dependencies:

    npm install dotenv ethers
  2. Create a .env file:

    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...
  3. 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 or int(time.time() * 1000) in Python
  • All signed message payloads must include timestamp field