POST /withdrawals
Submit a withdrawal
Submit a withdrawal.
operationId: createWithdrawal · CCXT method: withdraw
Authentication: none declared, and the contract declares no document-level default — this operation carries its own signature in the request body. See the description above.
Withdraws USDX collateral from the venue back to the signing wallet.
The signature is the authentication, not an API key. This operation takes no X-API-Key and no session token: the caller signs an EIP-712 WithdrawIntent with the wallet's own key and the server recovers the signer from signature. The recovered address must equal wallet, or the request is refused with 401 SIGNER_MISMATCH. security is omitted rather than [] for that reason — the operation is not public, it is authorized by something this document has no scheme for, the same way POST /agents/register expresses it.
EIP-712 domain: { name: 'Nexus Exchange', version: '1', chainId: 20056 }. Unlike POST /agents/register, the chain id is not a request field here: the server always verifies against 20056, so a signature produced under any other domain simply does not recover wallet and is refused with 401 SIGNATURE_INVALID. Sign with that value; do not substitute a network chain id. Typed data type: WithdrawIntent { uint256 amount, address asset, uint64 nonce }, where asset is the USDX sentinel address 0xcccccccccccccccccccccccccccccccccccccccc. The venue is single-collateral and ignores the asset; the address exists only so the typed data has a stable binding.
The destination is implicit. Funds are credited to the signing wallet; the typed data carries no destination, so there is nothing to redirect. destination may be sent for clarity but must equal wallet — any other value is refused with 400 destination_locked.
nonce is single-use per wallet. Re-submitting an already-accepted (wallet, nonce) pair is refused with 401 NONCE_REPLAY and creates no second withdrawal, so a client that retries on an ambiguous network error cannot double-withdraw. This operation is not idempotent: a replay is rejected rather than answered with the original result.
amount is a whole smallest-unit count. It is a decimal string on the wire, but WithdrawIntent commits to a uint256, so a fractional value cannot be represented and is refused with 400 fractional_amount rather than rounded.
Exposure must be closed first. A withdrawal that would leave an open position under-collateralized is refused with 400 WITHDRAW_BLOCKED_BY_OPEN_POSITION; close positions and cancel resting orders before withdrawing.
Acceptance is not settlement. A 200 means the collateral was debited and the withdrawal was recorded with status pending; it does not mean funds have moved on-chain. Poll GET /withdrawals for the lifecycle status and for tx_hash, which stays null until the withdrawal is submitted on-chain.
Request body
Required. WithdrawalRequest — application/json.
{
"wallet": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"amount": "500",
"asset": "USDX",
"nonce": 1779225381434,
"signature": "0xdeadbeef..."
}Responses
200
Withdrawal accepted and collateral debited. The withdrawal is recorded with status pending; this is an acceptance, not an on-chain settlement.
400
The withdrawal was refused before or by the engine. Branch on code: - WITHDRAW_BLOCKED_BY_OPEN_POSITION — the account has open exposure; close it first. - INSUFFICIENT_BALANCE — the requested amount exceeds withdrawable collateral. - INVALID_AMOUNT — amount is absent, non-numeric, or not greater than zero. - fractional_amount — amount has a fractional part; WithdrawIntent carries a uint256 smallest-unit count. - destination_locked — destination was supplied and does not equal wallet. - BAD_WALLET — wallet is not a 0x-prefixed 20-byte address. - INVALID_JSON / BAD_REQUEST — the body could not be read or parsed. An engine rejection with no dedicated classification is forwarded unchanged; it carries the same value under both error and code.
401
The signature did not authorize this withdrawal. Branch on code: - NONCE_REPLAY — this (wallet, nonce) pair was already accepted. No second withdrawal was created; sign a fresh nonce. - SIGNER_MISMATCH — the address recovered from signature is not wallet. The response echoes the claimed and recovered addresses. - SIGNATURE_INVALID — signature is malformed, not 65 bytes, non-canonical (high-S), or recovered no key. - STORE_ERROR — the nonce could not be recorded, so the withdrawal was not attempted.
403
Withdrawals are refused for this wallet or this deployment. All three cases are decided before any signature work, so no nonce is consumed and a retry with the same signed intent is safe once the condition clears. Branch on code, or on error for the freeze envelope: - WITHDRAWALS_DISABLED (code) — the deployment-wide withdrawal kill switch is off. - withdrawals_frozen (error) — the exchange is frozen; balances remain tradeable but new outflows are suspended. - EARLY_ACCESS_REQUIRED (code) — the wallet is not on the early-access allowlist on a deployment that enforces one. Jurisdiction controls do not appear here: withdrawal paths are exempt from them by design, because a restriction that traps funds is worse than no restriction.
429
— (no schema declared)
—
Example
Generated from eng/apps/exchange/api/openapi.json (spec 0.9.36). Do not edit by hand — regenerate with python3 product/docs/tools/api-reference/generate.py. Hand-authored context lives in the Guides pages.
Last updated

