Authentication
All API requests must be authenticated using your API key and a request signature.
API Keys
Create API keys from the ZenOTC dashboard. Each key has:
- API Key: Public identifier included in requests
- API Secret: Private key used to sign requests (shown only once)
- Scopes: Permissions granted to the key
- IP Whitelist: Optional list of allowed IP addresses
Keep Your Secret Safe
Your API secret is shown only once when created. Store it securely and never share it or commit it to version control.
Required Headers
Every request must include these headers:
| Header | Description |
|---|---|
X-API-Key | Your API key |
X-API-Timestamp | Current Unix timestamp in milliseconds |
X-API-Signature | HMAC-SHA256 signature of the request |
Generating the Signature
The signature is computed as:
signature = HMAC-SHA256(api_secret, timestamp + method + path + body)
Where:
timestamp: Unix timestamp in milliseconds (same asX-API-Timestamp)method: HTTP method in uppercase (GET,POST, etc.)path: Request path starting with/(e.g.,/api/sdk/orders)body: Request body as JSON string (empty string for GET requests)
Python Example
import hmac
import hashlib
import time
import json
import requests
API_KEY = "your_api_key"
API_SECRET = "your_api_secret"
BASE_URL = "https://api.zenotc.com"
def generate_signature(method: str, path: str, body: str = "") -> tuple[str, str]:
"""Generate timestamp and signature for API authentication."""
timestamp = str(int(time.time() * 1000))
message = f"{timestamp}{method.upper()}{path}{body}"
signature = hmac.new(
API_SECRET.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return timestamp, signature
def api_request(method: str, endpoint: str, data: dict = None) -> dict:
"""Make an authenticated API request."""
body = json.dumps(data) if data else ""
timestamp, signature = generate_signature(method, endpoint, body)
headers = {
"X-API-Key": API_KEY,
"X-API-Timestamp": timestamp,
"X-API-Signature": signature,
"Content-Type": "application/json"
}
response = requests.request(
method=method,
url=f"{BASE_URL}{endpoint}",
headers=headers,
data=body if data else None
)
return response.json()
# Example: Get balances
balances = api_request("GET", "/api/sdk/portfolio/balances")
print(balances)
# Example: Create order
order = api_request("POST", "/api/sdk/orders", {
"side": "buy",
"asset": "BTC",
"quantity": 1.0,
"price": 50000.0
})
print(order)
JavaScript/Node.js Example
const crypto = require('crypto');
const axios = require('axios');
const API_KEY = 'your_api_key';
const API_SECRET = 'your_api_secret';
const BASE_URL = 'https://api.zenotc.com';
function generateSignature(method, path, body = '') {
const timestamp = Date.now().toString();
const message = `${timestamp}${method.toUpperCase()}${path}${body}`;
const signature = crypto
.createHmac('sha256', API_SECRET)
.update(message)
.digest('hex');
return { timestamp, signature };
}
async function apiRequest(method, endpoint, data = null) {
const body = data ? JSON.stringify(data) : '';
const { timestamp, signature } = generateSignature(method, endpoint, body);
const response = await axios({
method,
url: `${BASE_URL}${endpoint}`,
headers: {
'X-API-Key': API_KEY,
'X-API-Timestamp': timestamp,
'X-API-Signature': signature,
'Content-Type': 'application/json'
},
data: data || undefined
});
return response.data;
}
// Example: Get balances
const balances = await apiRequest('GET', '/api/sdk/portfolio/balances');
// Example: Create order
const order = await apiRequest('POST', '/api/sdk/orders', {
side: 'buy',
asset: 'BTC',
quantity: 1.0,
price: 50000.0
});
cURL Example
#!/bin/bash
API_KEY="your_api_key"
API_SECRET="your_api_secret"
BASE_URL="https://api.zenotc.com"
METHOD="GET"
ENDPOINT="/api/sdk/portfolio/balances"
BODY=""
TIMESTAMP=$(date +%s%3N)
MESSAGE="${TIMESTAMP}${METHOD}${ENDPOINT}${BODY}"
SIGNATURE=$(echo -n "$MESSAGE" | openssl dgst -sha256 -hmac "$API_SECRET" | cut -d' ' -f2)
curl -X $METHOD "${BASE_URL}${ENDPOINT}" \
-H "X-API-Key: $API_KEY" \
-H "X-API-Timestamp: $TIMESTAMP" \
-H "X-API-Signature: $SIGNATURE"
Timestamp Validation
Requests are rejected if the timestamp is more than 30 seconds from the server time. Ensure your system clock is synchronized.
Error response for expired timestamp:
{
"statusCode": 401,
"message": "Request timestamp expired",
"error": "Unauthorized"
}
Authentication Errors
| Error | Description |
|---|---|
INVALID_API_KEY | API key not found or has been revoked |
INVALID_SIGNATURE | Signature verification failed |
TIMESTAMP_EXPIRED | Request timestamp is too old |
INSUFFICIENT_SCOPE | API key doesn't have required permission |
IP_NOT_WHITELISTED | Request IP not in key's whitelist |
Example error response:
{
"statusCode": 401,
"message": "Invalid API key",
"error": "Unauthorized",
"code": "INVALID_API_KEY"
}
Best Practices
- Use environment variables for API credentials
- Rotate keys regularly and revoke unused keys
- Use IP whitelisting in production
- Request minimal scopes needed for your use case
- Handle authentication errors gracefully with retries