API Authorization
Overview
The system provides API Key + Secret signature-based authentication for Institution users. Developers must sign each request using their API Key and API Secret. The signature is generated by concatenating the request method, request path, maker address (if applicable), timestamp, and request parameters/body with | delimiter, then applying the HMAC-SHA256 hash algorithm. The signature and related metadata are included in the request headers. The server validates the signature, authenticates the user, and authorizes before processing the request.
API endpoints are divided into two authorization levels based on endpoint type:
| Scope | X-Maker-Addr Required |
|---|---|
| Maker API: Order, position, corporate action, and other Maker-related endpoints | Yes — Must be included in request headers and signature string |
| Non-Maker API: Market data queries, and other Maker-unrelated endpoints | No — Omit from request headers and use empty string in signature string |
Authentication Flow
Maker API Endpoints
Used for order, position, corporate action, and other Maker-related endpoints. Each request must include:
- API Key
- Maker Address (
X-Maker-Addr) - Timestamp
- Signature calculated using API Secret and request content (signature string includes Maker Address)
The API validates:
- API Key is valid and active
- Signature matches the request content
- Timestamp is within allowed range (default tolerance ±5 minutes)
- X-Maker-Addr header is present and valid
Non-Maker API Endpoints
Used for market data queries, and other Maker-unrelated endpoints. Requests do not require X-Maker-Addr:
- API Key
- Timestamp
- Signature calculated using API Secret and request content (signature string does not include Maker Address)
The API validates:
- API Key is valid and active
- Signature matches the request content
- Timestamp is within allowed range (default tolerance ±5 minutes)
If any validation fails, the request returns an error code, such as:
{"code":10010008,"message":"Signature verification failed"}Required Request Headers
All authenticated API requests must include the following custom headers:
| Header Name | Example Value | Description |
|---|---|---|
X-API-Key | A1B2C3D4E5F6... | Your public API key |
X-API-Signature | a8f9c3e... | HMAC-SHA256 generated signature |
X-API-Timestamp | 1715100000000 | Current UNIX timestamp in milliseconds |
Conditionally Required Header
The following header is required only for Maker API endpoints:
| Header Name | Example Value | Description |
|---|---|---|
X-Maker-Addr | 0x1234567890... | Your institution maker address |
When accessing non-Maker API endpoints, the
X-Maker-Addrheader is not required and will be ignored if included.
Optional (recommended for tracking):
| Header Name | Example Value | Description |
|---|---|---|
X-REQUEST-ID | uuid-string | Client-defined request ID for debugging and log correlation |
Signature Generation Steps
Use uppercase HTTP request method (e.g.,
POST).Concatenate with
|the API path (without domain and protocol, must start with/, e.g.,/api/v1/orders).Append
|and the Maker Address (X-Maker-Addrvalue) — only for Maker API endpoints. For non-Maker API endpoints, leave this position empty (i.e., two consecutive|).Append
|and the UNIX timestamp in milliseconds.Append the request parameters:
- GET requests: Use
key=valueconcatenated query parameters (without?), empty string if none. Note: Do not change the order of parameters, ensure the signed and requested parameters are consistent. - Non-GET requests: Use raw JSON string as body, empty string if none. Note: Ensure the request body and signature are consistent, do not change due to formatting, causing the request and signature to be different.
- GET requests: Use
Final signature string format:
Maker API endpoints (requires X-Maker-Addr):
{request_method}|{request_path}|{maker_addr}|{timestamp}|{query_string_or_request_body}Non-Maker API endpoints (no X-Maker-Addr, Maker Address position left empty):
{request_method}|{request_path}||{timestamp}|{query_string_or_request_body}Use API Secret to encrypt this signature string with HMAC-SHA256.
Base64 encode the result.
Add the following headers to the request:
X-API-KeyX-API-TimestampX-API-SignatureX-Maker-Addr(only for Maker API endpoints)
Example (Signature String Construction):
Maker API endpoint (e.g., create order, requires X-Maker-Addr):
POST|/api/v1/orders|0x1234567890123456789012345678901234567890|1746774142003|{"order":{"maker":"..."}}Non-Maker API endpoint (e.g., query stock token address, no X-Maker-Addr):
GET|/api/v1/instruments/token-address||1746774142003|ticker=AAPLs&exchange=0Final signature:
Base64(HMAC-SHA256(signature_string, API_Secret))Code Examples
import requests
import hmac
import hashlib
import base64
import time
class InstitutionApiClient:
def __init__(self, api_key, api_secret, maker_addr, base_url):
self.api_key = api_key
self.api_secret = api_secret
self.maker_addr = maker_addr
self.base_url = base_url
def send_request(self, method, path, query_string='', body='', require_maker_addr=True):
"""
require_maker_addr: True for Maker API endpoints, False for non-Maker endpoints
"""
timestamp = int(time.time() * 1000) # Milliseconds
maker_addr = self.maker_addr if require_maker_addr else ''
signature_string = self.build_signature_string(method, path, timestamp, query_string, body, maker_addr)
signature = self.generate_signature(signature_string, self.api_secret)
url = self.base_url.rstrip('/') + '/' + path.lstrip('/')
if query_string:
url += '?' + query_string
headers = {
'X-API-Key': self.api_key,
'X-API-Timestamp': str(timestamp),
'X-API-Signature': signature,
'Content-Type': 'application/json'
}
if require_maker_addr:
headers['X-Maker-Addr'] = self.maker_addr
if method.upper() == 'GET':
response = requests.get(url, headers=headers)
else:
response = requests.request(method.upper(), url, headers=headers, data=body)
return response.text
def build_signature_string(self, method, path, timestamp, query_string, body, maker_addr):
signature_string = f"{method.upper()}|{path}|{maker_addr}|{timestamp}"
if method.upper() == 'GET':
signature_string += f"|{query_string}"
else:
signature_string += f"|{body}"
return signature_string
def generate_signature(self, data, secret):
key = secret.encode('utf-8')
message = data.encode('utf-8')
hmac_obj = hmac.new(key, message, hashlib.sha256)
return base64.b64encode(hmac_obj.digest()).decode('utf-8')
# Usage example
api_key = 'your_api_key_here'
api_secret = 'your_api_secret_here'
maker_addr = '0x1234567890123456789012345678901234567890'
base_url = '{API_BASE_URL}'
client = InstitutionApiClient(api_key, api_secret, maker_addr, base_url)
# Create order example (Maker API, requires X-Maker-Addr)
order_body = '{"order":{"maker":"0x1234567890123456789012345678901234567890","principal":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd","is_buy":true,"ticker":"AAPLs","exchange":0,"chain_id":56,"asset":"0x0000000000000000000000000000000000000001","price":"150250000000000000000","quantity":"100","incentive":"1000000000000000000","deadline":1735689600,"nonce":1},"signature":"0x..."}'
response = client.send_request('POST', '/api/v1/orders', body=order_body, require_maker_addr=True)
print('Create Order Response:', response)
# Query orders example (Maker API, requires X-Maker-Addr)
query_response = client.send_request('GET', '/api/v1/orders', 'ticker=AAPLs&page=1&page_size=20', require_maker_addr=True)
print('Query Response:', query_response)
# Market data query example (non-Maker API, no X-Maker-Addr)
market_response = client.send_request('GET', '/market/v1/tickers/quotes', 'market=usex&symbols=AAPLs,TSLAs', '', require_maker_addr=False)
print('Market Response:', market_response)WebSocket Authentication
The WebSocket endpoint /ws/v2/stream uses a message-level institution credential authentication scheme. Authentication information is transmitted via the api_key, timestamp, and signature fields within each message body rather than through connection headers.
Credential Acquisition
Institution users must first apply for API credentials through the admin panel to obtain a pair of api_key and secret values. The secret is only used for signature calculation and must never be transmitted over the network.
Signature Algorithm
The signature algorithm uses HMAC-SHA256, with the result encoded as a lowercase hexadecimal string. This differs from the REST API signature which uses Base64 encoding.
Client Signing Steps
- Construct the complete JSON message body with the
signaturefield set to an empty string""(as a signature placeholder) - Serialize the JSON object to a string as the data to sign
- Compute HMAC-SHA256 on the data using the
secretas the key - Convert the HMAC result to a lowercase hexadecimal string to obtain the signature value
- Replace the empty string placeholder in the
signaturefield with the computed signature - Send the complete JSON message to the server
Client Signature Example
import hmac
import hashlib
import json
import time
# 1. Prepare message (signature left empty)
message = {
"id": "550e8400-e29b-41d4-a716-446655440000",
"method": "market",
"action": "subscribe",
"params": {
"type": "orderbooks",
"tickers": ["AAPLs@usex"]
},
"api_key": "your-api-key",
"timestamp": str(int(time.time() * 1000)),
"signature": "" # Signature placeholder, must be empty string
}
# 2. Serialize JSON as data to sign
# Use separators=(',', ':') for compact representation
data_to_sign = json.dumps(message, separators=(",", ":"))
# 3. Compute HMAC-SHA256, output lowercase hex
secret = "your-secret"
sig = hmac.new(
secret.encode("utf-8"),
data_to_sign.encode("utf-8"),
hashlib.sha256
).hexdigest().lower()
# 4. Fill in the signature value
message["signature"] = sig
# 5. Send
final_json = json.dumps(message, separators=(",", ":"))
websocket.send(final_json)Server Verification Steps
- Receive the complete JSON message sent by the client
- Extract the
api_key,timestamp, andsignaturefields from the message - Verify timestamp:
timestampmust not deviate from the current server time by more than ±5 minutes to prevent replay attacks - Look up credentials: Find the corresponding institution credentials in the database by
api_key, retrieving the associatedsecretand user information (UserId, WalletAddress, Role, etc.) - Verify environment match: If the credentials are tagged with a specific environment (e.g., production only), check if the current runtime environment matches
- IP whitelist check (production environment): If the credentials have IP whitelist enabled, verify the client IP is in the whitelist (internal IPs are automatically exempted)
- Verify signature:
- Replace the
signaturefield value in the original JSON text with an empty string, obtaining the pre-signature data - Compute HMAC-SHA256 on the pre-signature data using the
secretin the credentials, converting the result to lowercase hexadecimal - Compare the computed result with the
signatureprovided by the client; authentication passes if they match
- Replace the
Note: In step 7, the server uses the original JSON text with the signature field value removed rather than re-serializing from a deserialized object. This ensures exact consistency with the client's signing string (avoiding signature verification failures due to serialization differences).
Authentication Failure
When authentication fails, the server returns:
{
"id": "...",
"success": false,
"message": "Invalid or missing authentication header(s)"
}Possible causes include: invalid or nonexistent api_key, timestamp outside the ±5-minute range, signature mismatch, or IP not in whitelist.
Common Use Cases
Maker API Scenarios (requires X-Maker-Addr)
Create Order
curl -X POST "{API_BASE_URL}/api/v1/orders" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Maker-Addr: YOUR_MAKER_ADDRESS" \
-H "X-API-Timestamp: 1715100000000" \
-H "X-API-Signature: GENERATED_SIGNATURE" \
-H "Content-Type: application/json" \
-d '{"order":{"maker":"0x1234567890123456789012345678901234567890","principal":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd","is_buy":true,"ticker":"AAPLs","exchange":0,"chain_id":56,"asset":"0x0000000000000000000000000000000000000001","price":"150250000000000000000","quantity":"100","incentive":"1000000000000000000","deadline":1735689600,"nonce":1},"signature":"0x..."}'Cancel Order
curl -X POST "{API_BASE_URL}/api/v1/orders/{order_id}/cancel" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Maker-Addr: YOUR_MAKER_ADDRESS" \
-H "X-API-Timestamp: 1715100000000" \
-H "X-API-Signature: GENERATED_SIGNATURE" \
-H "Content-Type: application/json"Query Orders
curl -X GET "{API_BASE_URL}/api/v1/orders?ticker=AAPLs&page=1&page_size=20" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Maker-Addr: YOUR_MAKER_ADDRESS" \
-H "X-API-Timestamp: 1715100000000" \
-H "X-API-Signature: GENERATED_SIGNATURE"Non-Maker API Scenarios (no X-Maker-Addr)
Market Data — Batch Quotes
curl -X GET "{API_BASE_URL}/market/v1/tickers/quotes?market=usex&symbols=AAPLs,TSLAs" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Timestamp: 1715100000000" \
-H "X-API-Signature: GENERATED_SIGNATURE"Note the signature string for non-Maker API requests is
GET|/market/v1/tickers/quotes||1715100000000|market=usex&symbols=AAPLs,TSLAs(Maker Address position left empty), and the request does not include theX-Maker-Addrheader.
Instrument Management — Query Stock Token Address
curl -X GET "{API_BASE_URL}/api/v1/instruments/token-address?ticker=AAPLs&exchange=0" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Timestamp: 1715100000000" \
-H "X-API-Signature: GENERATED_SIGNATURE"Error Codes
| Code | Message | Description | Solution |
|---|---|---|---|
| 10010008 | Signature verification failed | Signature does not match | Check signature generation logic, ensure parameters are consistent; for non-Maker API endpoints, verify the Maker Address position is empty in the signature string |
| 10010009 | API key not found | API key does not exist | Verify API key is correct |
| 10010010 | API key expired | API key has expired | Contact administrator to renew API key |
| 10010011 | Timestamp expired | Request timestamp outside allowed range | Ensure system time is synchronized, regenerate timestamp |
| 10010012 | Missing required header | Missing authentication header | Ensure all required headers are included; Maker API endpoints must include X-Maker-Addr |
| 10010013 | Invalid maker address | Maker address is invalid or not authorized | Verify the X-Maker-Addr header is correct and authorized |
| 10010014 | Permission denied | Insufficient permissions | Maker API endpoints missing X-Maker-Addr header, or API Key lacks access to this resource |
The above error codes are returned in the JSON response body as
{"code": <code>, "message": "..."}. The API may also return standard HTTP status codes (e.g.,401 Unauthorized,403 Forbidden) at the HTTP level for authentication or authorization failures.
Security Best Practices
For API Key Management
Secure Storage
- Never hardcode API keys in source code
- Use environment variables or secure configuration management
- Rotate API keys regularly
Access Control
- Use different API keys for different environments (dev, staging, prod)
- Implement IP whitelist if possible
- Monitor API key usage and set up alerts
Secret Protection
- Never expose API secret in client-side code
- Never log API secret in application logs
- Use HTTPS for all API communications
For Signature Generation
Timestamp Validation
- Ensure system time is synchronized with NTP server
- Handle clock skew appropriately
- Regenerate signature for retry requests
Parameter Consistency
- Do not modify request parameters after signature generation
- Maintain parameter order for GET requests
- Avoid formatting JSON body after signature generation
- Maker API endpoints: ensure
X-Maker-Addris included in signature string and matches the header value - Non-Maker API endpoints: ensure the Maker Address position is left empty in the signature string, and do not include the
X-Maker-Addrheader in the request
Error Handling
- Implement exponential backoff for failed requests
- Log signature verification failures for debugging
- Do not retry with same signature if timestamp expired
Troubleshooting
Issue: Signature verification failed
Possible causes:
- Incorrect API secret
- Parameter order mismatch
- JSON body formatting differences
- Timestamp format incorrect
- Maker API endpoint:
X-Maker-Addrnot included in signature string, or positioned incorrectly in the string - Non-Maker API endpoint:
X-Maker-Addrmistakenly included in signature string
Solution:
- Verify API secret is correct
- Log the signature string before hashing
- Ensure request body matches signed body exactly
- Use millisecond timestamp (not seconds)
- Maker API endpoint: confirm
X-Maker-Addris correctly placed between path and timestamp in the signature string - Non-Maker API endpoint: confirm the Maker Address position is empty (
||) in the signature string, and the header is not included in the request
Issue: Timestamp expired
Possible causes:
- System clock not synchronized
- Request took too long to send
- Reusing old signature
Solution:
- Synchronize system time with NTP
- Generate new signature for each request
- Reduce network latency
Issue: API key not found
Possible causes:
- Incorrect API key
- API key deleted or expired
- Wrong environment
Solution:
- Verify API key from admin panel
- Check if using correct environment (test vs prod)
- Contact administrator if key is missing