Endpoint Security Type
ZTDX accepts two authentication mechanisms. Each endpoint declares which one it requires:
| Mechanism | Header / Body | Used by |
|---|---|---|
| HMAC SHA-256 signature | X-MBX-APIKEY: <api_key> + signature | /fapi/v1/* programmatic / market-making clients |
| EIP-712 wallet signature | Authorization: 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
- Create an API Key. See Quick Start.
- Build a string to sign — for
GET/DELETE, the URL query string (e.g.symbol=BTCUSDT&side=BUY×tamp=…); forPOST/PUT, the query string concatenated with the request body. - Compute
HMAC_SHA256(secret_key, payload)and append it to the URL as&signature=<hex>. - Send the request with
X-MBX-APIKEY: <api_key>.
Required parameters
| Name | Description |
|---|---|
timestamp | Unix milliseconds, must lie within [serverTime − 60000ms, serverTime + 60000ms] (fixed ±60s window, enforced in both directions) |
recvWindow | Accepted for Binance compatibility but ignored — the window is fixed at ±60000ms and cannot be tightened or loosened. |
signature | The 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.prex.world"
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)
- Fetch a nonce:
GET /api/v1/auth/nonce/:addressreturns the user's currentnoncetogether with a ready-to-sign EIP-712typed_dataobject (typeLogin(address wallet,uint256 nonce,uint256 timestamp)). A user row is created automatically on first call. - Sign the typed data with the wallet (e.g.
eth_signTypedData_v4). - Exchange for a JWT:
POST /api/v1/auth/loginwith{ "address", "signature", "timestamp" }. Thetimestamp(Unix seconds) must be within ±300 s of server time, otherwise the request fails with400 TIMESTAMP_EXPIRED. On success the nonce is incremented (each signature is single-use) and the response is{ "token", "expires_at" }. - The JWT is valid for
86400seconds (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 callers | Works (the documented/primary use) |
JWT (Authorization: Bearer) | Works (the documented/primary use); body EIP-712 signature required on trading actions | Works |
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.