APIKEY header — a request carrying it is verified as HMAC and is
never retried as a session, even if the signature is bad.
Session tokens
Exchange an email and password for a JWT:read and trade scopes — never
withdraw.
API keys
Mint a key from a session:secret is returned; store it then.
Keys minted from a session are capped at the session’s own scopes (read,
trade), so a session can never mint a withdraw-scoped key.
Scopes
Signing requests
Each HMAC request sends three headers:
The canonical message is:
PATH_AND_QUERYis the full request target including the query string —/rfq/quote?request_id=..., not/rfq/quote. Every GET endpoint takes its parameters from the query string, so they are inside the signature.BODYis the exact raw bytes you transmit. Sign the serialized string you send, not a re-serialization. For bodyless requests (GETs, DELETEs without a payload) the body segment is empty — the message still ends with the third\n.- The timestamp must be within ±30 seconds of server time (the deployment’s
AUTH_HMAC_TOLERANCE_SECS), which bounds the replay window.
Failure modes
A common cause of
401 is signing path while sending path?query, or letting
an HTTP client re-serialize the JSON body after signing.
Websocket tickets
Browsers cannot set headers on websocket upgrades, so socket connections authenticate with a short-lived ticket passed as a?ticket= query parameter:
POST /auth/ws-ticket— from a session; ticket carriesread.POST /auth/ws-ticket/signed— HMAC-signed; ticket inherits the key’s scopes.