Authentication
HMAC-SHA256 signing rules, nonce-based replay protection, and the three credentials you receive at onboarding.
Every authenticated request carries three headers: X-API-KEY, X-API-SIGN, X-API-NONCE. Sign the exact bytes you send — that’s the whole rule. Same in bash, Python, Node; see the worked example below, or jump to the signing template.
Required headers
| Header | Required | Description |
|---|---|---|
| X-API-KEY | Yes | Public partner key. May carry an optional code segment: apiKey.codeName— see /v1/codes. |
| X-API-SIGN | Yes | Hex signature of the raw request body, keyed by your apiSecret. The recipe is in the worked example below. |
| X-API-NONCE | Yes | Unique per request (UUID or 16-byte hex). Reusing a nonce returns code 3 AUTH_INVALID. |
Signing rules
- Sign the exact bytes you put on the wire.
- For
GET /v1/codesthe request has no body. Sign the empty string"": same HMAC primitive, zero input bytes.DELETE /v1/codescarries a JSON body{"code":"<code>"}. Sign that exact body, not the empty string. - Bad signature, unknown key, replayed nonce, and malformed envelope all collapse to the same response:
{ code: 3, msg: "AUTH_INVALID" }. You learn whether you got in, never which check failed.
Replay protection
Nonces are tracked server-side. Use a fresh random value per request, 16 to 64 characters. A UUID without hyphens is 32; with hyphens, 36; 64-char hex is also valid. Reuse, under-16, or over-64 all return { code: 3, msg: "AUTH_INVALID" }. There is no timestamp header; caller and server clocks are never compared.
Unauthenticated endpoints
/v1/currencies, /v1/pairs, and /v1/price accept unsigned requests for catalog and un-attributed quoting. Unsigned traffic runs on a separate public bucket and never charges a partner budget. Signed /v1/price calls receive partner-specific markup. Public limits are on the Rate limits page.
End-to-end example
POST /api/v1/price in three languages. The signature is computed over the exact JSON body bytes you send.
# Request a quote for 0.01 BTC -> USDT (Tron) with a 0.5% partner markup.
APIKEY="rWqZ...your-api-key...Rg"
APISECRET="G1JV...your-api-secret...n4"
NONCE=$(openssl rand -hex 16)
BODY='{"type":"float","fromCcy":"btc","toCcy":"usdt_trc20","direction":"from","amount":"0.01","afftax":50}'
SIGN=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$APISECRET" | sed 's/^.* //')
curl -sS -X POST "https://0trace.io/api/v1/price" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $APIKEY" \
-H "X-API-SIGN: $SIGN" \
-H "X-API-NONCE: $NONCE" \
--data "$BODY"Common mistakes
- Re-serialising the body before sending. If your HTTP client pretty-prints or sorts keys before transmission, the body on the wire differs from the bytes you signed. Always sign the exact bytes you POST.
- Mixing keys across requests. The
X-API-KEYheader and the secret you HMAC with are a pair, issued together at onboarding. The header itself is not part of the HMAC input; only the raw request body is. Use theapiSecretthat was provisioned alongside theapiKeyyou send. - Stale, short, or oversized nonces. Generate a fresh random nonce per request, 16–64 characters inclusive. Reusing a value, sending an empty or under-16-character nonce, or sending an over-64-character nonce all return the same
X-API-NONCEAUTH_INVALID envelope.
Credential provisioning
Onboarding gives you three secrets through a secure channel:
{
"apiKey": "rWqZ...your-public-key...Rg",
"apiSecret": "G1JV...your-signing-secret...n4",
"webhookSecret": "Lh2P...your-webhook-secret...8K"
}Rotate both at any time through /v1/keys/rotate. The previous value is invalidated immediately.