Skip to main content

Endpoint Security Type

ZTDX accepts two authentication mechanisms. Each endpoint declares which one it requires:

MechanismHeader / BodyUsed by
HMAC SHA-256 signatureX-MBX-APIKEY: <api_key> + signature/fapi/v1/* programmatic / market-making clients
EIP-712 wallet signatureAuthorization: Bearer <JWT> (after login)/api/v1/* interactive / browser clients

Public endpoints (market data, exchange info, public leaderboards) require no signature.

HMAC SHA-256 Signature​

This is the Binance-compatible signing flow. Use it for any client that trades programmatically.

Steps​

  1. Create an API Key. See Quick Start.
  2. Build a string to sign — for GET / DELETE, the URL query string (e.g. symbol=BTCUSDT&side=BUY&timestamp=…); for POST / PUT, the query string concatenated with the request body.
  3. Compute HMAC_SHA256(secret_key, payload) and append it to the URL as &signature=<hex>.
  4. Send the request with X-MBX-APIKEY: <api_key>.

Required parameters​

NameDescription
timestampUnix milliseconds, must lie within [serverTime − 60000ms, serverTime + 60000ms] (fixed ±60s window, enforced in both directions)
recvWindowAccepted for Binance compatibility but ignored — the window is fixed at ±60000ms and cannot be tightened or loosened.
signatureThe hex-encoded HMAC SHA-256

A request whose timestamp falls outside the window returns HTTP 401 with the standard error envelope:

{
"success": false,
"data": null,
"error": {
"code": "SIGNATURE_INVALID",
"message": "Timestamp outside recv window"
},
"timestamp": 1778716800
}

Payload encoding tolerance​

Some HTTP libraries URL-encode special characters in the query string (e.g. [ → %5B, , → %2C) before hashing; others sign the raw form. ZTDX accepts both: the server tries the raw payload first and falls back to a URL-decoded comparison if that fails. Clients do not need to align with a specific encoding before signing.

Example (Python)​

import time, hmac, hashlib, requests

API_KEY = "your_api_key"
API_SECRET = "your_api_secret"
BASE_URL = "https://api.ztdx.io"

def sign(payload: str) -> str:
return hmac.new(API_SECRET.encode(), payload.encode(), hashlib.sha256).hexdigest()

def signed_get(path: str, params: dict):
params["timestamp"] = int(time.time() * 1000)
qs = "&".join(f"{k}={v}" for k, v in params.items())
return requests.get(
f"{BASE_URL}{path}?{qs}&signature={sign(qs)}",
headers={"X-MBX-APIKEY": API_KEY},
)

print(signed_get("/fapi/v1/openOrders", {"symbol": "BTCUSDT"}).json())

EIP-712 Wallet Signature​

Used by the front-end and any client that authenticates with the user's wallet.

Login flow (nonce → typed-data sign → JWT)​

  1. Fetch a nonce: GET /api/v1/auth/nonce/:address returns the user's current nonce together with a ready-to-sign EIP-712 typed_data object (type Login(address wallet,uint256 nonce,uint256 timestamp)). A user row is created automatically on first call.
  2. Sign the typed data with the wallet (e.g. eth_signTypedData_v4).
  3. Exchange for a JWT: POST /api/v1/auth/login with { "address", "signature", "timestamp" }. The timestamp (Unix seconds) must be within ±300 s of server time, otherwise the request fails with 400 TIMESTAMP_EXPIRED. On success the nonce is incremented (each signature is single-use) and the response is { "token", "expires_at" }.
  4. The JWT is valid for 86400 seconds (24 h) by default.

Once issued, the JWT is sent as Authorization: Bearer <jwt>. Most trading actions on /api/v1/* additionally require an EIP-712 signature inside the request body that covers the action's structured payload — each endpoint's page lists its TypeHash.

Which mechanism works on which routes​

Both authentication mechanisms are handled by the same middleware, which is mounted on the protected routes of both surfaces (/api/v1/* and /fapi/*). In practice:

Credential/api/v1/*/fapi/v1/*
API Key + HMAC (X-MBX-APIKEY)Works — and the per-request EIP-712 body signature is waived for API-key callersWorks (the documented/primary use)
JWT (Authorization: Bearer)Works (the documented/primary use); body EIP-712 signature required on trading actionsWorks

The middleware checks X-MBX-APIKEY first; if the header is absent it falls back to Bearer-JWT validation. The table on top of this page describes the intended pairing, not a hard restriction.