Request Withdrawal Signature
Description
Reserves the requested amount of DF, signs an EIP-712 SpotReleaseFunds release authorization, and returns the data the caller needs to submit to the vault contract on-chain.
This is step 1 of the two-step withdrawal flow. After receiving the signature, the caller submits it to ZtdxSpotVault.withdraw() on BSC. For the full flow including the vault call and status polling, see Withdraw Flow.
Atomicity: Within a single database transaction, available is decremented and frozen is incremented by amount. If the on-chain call is not made before deadline, a reaper task marks the record expired and returns the funds to available. See Withdraw Flow → Expiry Reaper.
MVP supports only DF.
HTTP Request
POST /spot/withdraw/request (JWT only)
API-key callers receive 403 API Key permission denied: withdraws not allowed. See General Info → Authentication.
Weight
0 — no per-IP weight limit today (MVP).
Request Parameters
| Name | Type | Required | Description |
|---|---|---|---|
token | STRING | YES | Token to withdraw. MVP: DF only. |
amount | DECIMAL | YES | Decimal amount as a string (e.g. "100"). Must be at least the server-configured minimum (SPOT_WITHDRAW_MIN_AMOUNT_DF). |
There is no recipient parameter. The EIP-712 account field is always set to the caller's address, and the vault checks msg.sender == account — a third party cannot replay the signature for a different recipient.
Response Example
200 OK
{
"id": "9f2a1c4e-5b67-4d8a-bf93-2e1f4a6c8d10",
"nonce": 42,
"signature": "0x...65-byte hex...",
"deadline": 1778402000,
"vault_address": "0x4Fe0b354c5865ee9deb979a99030d757ae47664a",
"chain_id": 97,
"amount": "100",
"amount_in_wei": "100000000000000000000"
}
| Field | Notes |
|---|---|
id | UUID of the withdrawal record. Use with GET /spot/withdrawals/:id to poll status. |
nonce | Per-(user, chain) monotonic nonce. Prevents on-chain replay. |
signature | 65-byte 0x-prefixed EIP-712 signature. Pass verbatim to vault.withdraw(). |
deadline | Unix seconds. The on-chain withdraw() call must execute strictly before this timestamp. |
vault_address | The ZtdxSpotVault contract address to call withdraw() on. |
chain_id | EVM network the signature targets — 97 (BSC Testnet). |
amount | Decimal string of the reserved amount. |
amount_in_wei | Wei-scaled amount placed in the EIP-712 value field. Feed this into vault.withdraw() — preferred over amount because it removes decimal/precision ambiguity. |
The response does not include token or status fields — the record is always signed on success; fetch GET /spot/withdrawals/:id for the full record.
Error Responses
| HTTP | error |
|---|---|
400 | unsupported token: <token> — token other than DF requested. |
400 | amount below minimum <min> — amount is below the server-configured minimum. There is no separate non-positive check — zero or negative amounts also fail this check. |
400 | insufficient balance — spot available < amount. |
400 | invalid user address — the authenticated address could not be parsed. |
403 | API Key permission denied: withdraws not allowed — caller authenticated via API Key. |
409 | you have a pending withdrawal — submit it on-chain or wait for it to expire before signing a new one — a signed withdrawal at or after the contract's current nonce already exists. |
409 | withdrawal slot conflict — try again in a moment — the nonce slot is occupied by a non-expired row. |
500 | chain query failed — reading the vault's on-chain nonce failed. |
500 | signer unavailable / sign failed — the backend signer could not be initialized or signing failed. |
500 | internal — unexpected database error; investigate logs. |
503 | spot subsystem disabled — server has the spot subsystem turned off. |
503 | spot blockchain not initialized — the BSC blockchain service is not running. |
Full list: Error Codes.
Code Examples
cURL (JWT)
JWT="your_jwt_token"
curl -s -X POST "https://api.prex.world/api/v1/spot/withdraw/request" \
-H "Authorization: Bearer ${JWT}" \
-H "Content-Type: application/json" \
-d '{
"token": "DF",
"amount": "100"
}'
Python
import requests
BASE_URL = "https://api.prex.world/api/v1"
JWT = "your_jwt_token"
resp = requests.post(
f"{BASE_URL}/spot/withdraw/request",
headers={
"Authorization": f"Bearer {JWT}",
"Content-Type": "application/json",
},
json={"token": "DF", "amount": "100"},
timeout=5,
)
resp.raise_for_status()
sig = resp.json()
print(f"withdrawal id : {sig['id']}")
print(f"nonce : {sig['nonce']}")
print(f"deadline : {sig['deadline']}")
print(f"signature : {sig['signature'][:20]}...")
# Next: call vault.withdraw(DF_TOKEN, amount_in_wei, deadline, signature)
# See withdraw-flow.md for the full end-to-end code example.