Skip to main content

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:

HeaderDescription
X-API-KeyYour API key
X-API-TimestampCurrent Unix timestamp in milliseconds
X-API-SignatureHMAC-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 as X-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

ErrorDescription
INVALID_API_KEYAPI key not found or has been revoked
INVALID_SIGNATURESignature verification failed
TIMESTAMP_EXPIREDRequest timestamp is too old
INSUFFICIENT_SCOPEAPI key doesn't have required permission
IP_NOT_WHITELISTEDRequest IP not in key's whitelist

Example error response:

{
"statusCode": 401,
"message": "Invalid API key",
"error": "Unauthorized",
"code": "INVALID_API_KEY"
}

Best Practices

  1. Use environment variables for API credentials
  2. Rotate keys regularly and revoke unused keys
  3. Use IP whitelisting in production
  4. Request minimal scopes needed for your use case
  5. Handle authentication errors gracefully with retries