POST /api/v1/trading/withdraw is signed by an Ed25519 session key, the same as order entry and other cash writes. But the exchange holds withdrawals to a higher bar: the signing session must be admin-rooted, and a valid signature alone does not authorize one.
What “admin-rooted” means
A session is admin-rooted when both of these hold:- It was minted under an admin master key — not a scoped one.
- It is unpinned — its
scopeis the unpinned sentinel (the maximum 32-bit value), not a single subaccount index.
How to withdraw
Mint an unpinned session under an admin master key
Call
POST /api/v1/auth/sessions signed by an admin master key, with scope set to the unpinned sentinel (the maximum 32-bit value). See Session keys.Sign the withdrawal with that session
Build the
WithdrawCash payload and sign it with the Ed25519 session key (signature_type = 0). See Signed payloads.Submit
POST the signed envelope to
POST /api/v1/trading/withdraw. A rejection still returns HTTP 200 with success: false (or a rejected_* status) — read the body.Why the chain matters, not just the signature
The withdrawal authority lives in the master key, not the session. The exchange treats the session as a delegate: it checks the signature, then walks the chain to confirm the session descends from an admin master key and was never pinned. Both checks must pass. The same rule governs subaccount creation and admin-scope key management. The chain to an admin master key — not just a valid signature — authorizes account-level operations.The secrets-once implication
Keep the admin master key offline and mint short-lived sessions from it on demand. Every minted session secret is held only by you: the exchange never returns the session private key, and master-key material never leaves your control.- Keep the admin master key offline. It is the root of withdrawal authority. Generate and store its material off-exchange.
- Mint sessions on demand. Sessions are cheap and revocable. Mint an unpinned admin-rooted session when you need to withdraw, set a tight
valid_until, and revoke it when done.