Skip to main content
Authentication has two independent layers:
  1. Credential issuance. A signature proves control of a wallet once, and returns an API key and secret.
  2. Request signing. Every call carries an HMAC signature computed with that secret.
Orders add a third signature, from the wallet itself. That separation is deliberate: a leaked API secret cannot forge orders or move funds, because an order is valid only if it also carries the trading wallet’s signature. The blast radius of a leaked secret is reads and cancels.

Choosing a wallet

Every order is signed by the wallet that holds the collateral, because on-chain settlement checks that the order signature recovers to the trading wallet. The signing key must therefore be the key to the funds.
Generate or use an EOA whose private key is already held locally, fund it with USDC on Base, and configure the client against it.This is the recommended setup. The wallet is separate from the Noise app account, so a compromise of the client is contained to the capital held in that wallet.

Minting a credential

Sign an EIP-712 NoiseAuth attestation with the wallet and POST /keys. This route and POST /keys/rotate are the only authenticated routes that take no HMAC headers. The attestation signature is the authorization, which it must be, since no key exists yet. The domain, where chainId is exchange.attestationChainId from GET /status (configured separately from the order chain, even when the two match):
The message field must be exactly this string:
The response carries the key, the secret, and existing:
Issuance is derive-or-create: the secret is derived from the wallet, key, and nonce rather than stored, so repeating the call at the current nonce returns the same credentials with existing: true. A retry after a dropped response is therefore safe, and re-signing the same attestation recovers a lost secret.
apiSecret is returned only by POST /keys and POST /keys/rotate. GET /keys lists metadata only, so treat the secret as write-once: store it in a secret manager at mint time rather than planning to retrieve it later.

Scopes

trade implies read, because a client must reconcile its own fills. A read key suits accounting or dashboards; a read key that attempts to submit receives 403 FORBIDDEN.

Rotation and revocation

Rotate with POST /keys/rotate and a fresh attestation at nonce + 1. Rotating invalidates the old secret. When the current nonce is unknown, submit any nonce and read the NONCE_CONFLICT error, which names the nonce to use. Revoke with DELETE /keys/{apiKey}. A revoked key also closes any open WebSocket with code 4001.

Restricting a key by source address

Both POST /keys and POST /keys/rotate accept an optional ipAllowlist of up to 20 entries, enforced on both HTTP and the WebSocket. Matching is exact, so pass individual addresses, because a CIDR range is rejected rather than silently matching nothing. Omitting the field on rotate leaves an existing allowlist in place; passing an empty array clears it.

Signing requests

Every authenticated request carries these headers: The canonical message is four parts concatenated with no separators:
  • timestamp is the same value as the header.
  • METHOD is uppercased, for example POST.
  • path includes the query string, for example /api/trader/v1/account/orders?limit=50.
  • body is the raw request body exactly as sent, or an empty string for GET.
Sign the bytes actually transmitted. Re-serializing the body after signing is the most common cause of a 401: an HTTP client that reorders keys or alters whitespace invalidates the signature.
Clock drift presents as a 401. Where auth fails intermittently under load, check NTP on the calling machine before anything else, and raise x-noise-recv-window where latency to the API is genuinely high.