Skip to content
Stove Website

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:

ScopeX-Maker-Addr Required
Maker API: Order, position, corporate action, and other Maker-related endpointsYes — Must be included in request headers and signature string
Non-Maker API: Market data queries, and other Maker-unrelated endpointsNo — 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:

  1. API Key is valid and active
  2. Signature matches the request content
  3. Timestamp is within allowed range (default tolerance ±5 minutes)
  4. 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:

  1. API Key is valid and active
  2. Signature matches the request content
  3. Timestamp is within allowed range (default tolerance ±5 minutes)

If any validation fails, the request returns an error code, such as:

json
{"code":10010008,"message":"Signature verification failed"}

Required Request Headers ​

All authenticated API requests must include the following custom headers:

Header NameExample ValueDescription
X-API-KeyA1B2C3D4E5F6...Your public API key
X-API-Signaturea8f9c3e...HMAC-SHA256 generated signature
X-API-Timestamp1715100000000Current UNIX timestamp in milliseconds

Conditionally Required Header ​

The following header is required only for Maker API endpoints:

Header NameExample ValueDescription
X-Maker-Addr0x1234567890...Your institution maker address

When accessing non-Maker API endpoints, the X-Maker-Addr header is not required and will be ignored if included.

Optional (recommended for tracking):

Header NameExample ValueDescription
X-REQUEST-IDuuid-stringClient-defined request ID for debugging and log correlation

Signature Generation Steps ​

  1. Use uppercase HTTP request method (e.g., POST).

  2. Concatenate with | the API path (without domain and protocol, must start with /, e.g., /api/v1/orders).

  3. Append | and the Maker Address (X-Maker-Addr value) — only for Maker API endpoints. For non-Maker API endpoints, leave this position empty (i.e., two consecutive |).

  4. Append | and the UNIX timestamp in milliseconds.

  5. Append the request parameters:

    • GET requests: Use key=value concatenated 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.
  6. 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}
  1. Use API Secret to encrypt this signature string with HMAC-SHA256.

  2. Base64 encode the result.

  3. Add the following headers to the request:

    • X-API-Key
    • X-API-Timestamp
    • X-API-Signature
    • X-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=0

Final signature:

Base64(HMAC-SHA256(signature_string, API_Secret))

Code Examples ​

python
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 ​

  1. Construct the complete JSON message body with the signature field set to an empty string "" (as a signature placeholder)
  2. Serialize the JSON object to a string as the data to sign
  3. Compute HMAC-SHA256 on the data using the secret as the key
  4. Convert the HMAC result to a lowercase hexadecimal string to obtain the signature value
  5. Replace the empty string placeholder in the signature field with the computed signature
  6. Send the complete JSON message to the server

Client Signature Example ​

python
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 ​

  1. Receive the complete JSON message sent by the client
  2. Extract the api_key, timestamp, and signature fields from the message
  3. Verify timestamp: timestamp must not deviate from the current server time by more than ±5 minutes to prevent replay attacks
  4. Look up credentials: Find the corresponding institution credentials in the database by api_key, retrieving the associated secret and user information (UserId, WalletAddress, Role, etc.)
  5. Verify environment match: If the credentials are tagged with a specific environment (e.g., production only), check if the current runtime environment matches
  6. 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)
  7. Verify signature:
    • Replace the signature field 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 secret in the credentials, converting the result to lowercase hexadecimal
    • Compare the computed result with the signature provided by the client; authentication passes if they match

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:

json
{
  "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 ​

bash
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 ​

bash
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 ​

bash
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 ​

bash
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 the X-Maker-Addr header.

Instrument Management — Query Stock Token Address ​

bash
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 ​

CodeMessageDescriptionSolution
10010008Signature verification failedSignature does not matchCheck signature generation logic, ensure parameters are consistent; for non-Maker API endpoints, verify the Maker Address position is empty in the signature string
10010009API key not foundAPI key does not existVerify API key is correct
10010010API key expiredAPI key has expiredContact administrator to renew API key
10010011Timestamp expiredRequest timestamp outside allowed rangeEnsure system time is synchronized, regenerate timestamp
10010012Missing required headerMissing authentication headerEnsure all required headers are included; Maker API endpoints must include X-Maker-Addr
10010013Invalid maker addressMaker address is invalid or not authorizedVerify the X-Maker-Addr header is correct and authorized
10010014Permission deniedInsufficient permissionsMaker 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 ​

  1. Secure Storage

    • Never hardcode API keys in source code
    • Use environment variables or secure configuration management
    • Rotate API keys regularly
  2. 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
  3. 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 ​

  1. Timestamp Validation

    • Ensure system time is synchronized with NTP server
    • Handle clock skew appropriately
    • Regenerate signature for retry requests
  2. 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-Addr is 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-Addr header in the request
  3. 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-Addr not included in signature string, or positioned incorrectly in the string
  • Non-Maker API endpoint: X-Maker-Addr mistakenly included in signature string

Solution:

  1. Verify API secret is correct
  2. Log the signature string before hashing
  3. Ensure request body matches signed body exactly
  4. Use millisecond timestamp (not seconds)
  5. Maker API endpoint: confirm X-Maker-Addr is correctly placed between path and timestamp in the signature string
  6. 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:

  1. Synchronize system time with NTP
  2. Generate new signature for each request
  3. Reduce network latency

Issue: API key not found ​

Possible causes:

  • Incorrect API key
  • API key deleted or expired
  • Wrong environment

Solution:

  1. Verify API key from admin panel
  2. Check if using correct environment (test vs prod)
  3. Contact administrator if key is missing