Skip to main content

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. See General Info → Authentication.

Weight

0 — no per-IP weight limit today (MVP).

Request Parameters

NameTypeRequiredDescription
tokenSTRINGYESToken to withdraw. MVP: DF only.
amountDECIMALYESDecimal amount as a string (e.g. "100"). Must be ≥ 1 (minimum enforced server-side).
recipientSTRINGNOOn-chain recipient address. Defaults to the authenticated user's address. The EIP-712 account field is always set to the caller's address — a third party cannot replay the signature for a different recipient.

Response Example

200 OK

{
"id": "9f2a1c4e-5b67-4d8a-bf93-2e1f4a6c8d10",
"token": "DF",
"amount": "100",
"nonce": 42,
"signature": "0x...65-byte hex...",
"deadline": 1778402000,
"vault_address": "0x4Fe0b354c5865ee9deb979a99030d757ae47664a",
"chain_id": 97,
"status": "signed"
}
FieldNotes
idUUID of the withdrawal record. Use with GET /spot/withdrawals/:id to poll status.
tokenEchoes the request field.
amountDecimal string of the reserved amount.
noncePer-(user, chain) monotonic nonce. Prevents on-chain replay.
signature65-byte 0x-prefixed EIP-712 signature. Pass verbatim to vault.withdraw().
deadlineUnix seconds. The on-chain withdraw() call must execute strictly before this timestamp.
vault_addressThe ZtdxSpotVault contract address to call withdraw() on.
chain_idEVM network the signature targets — 97 (BSC Testnet).
statusAlways signed on a successful response.

Error Responses

HTTPerror
400AMOUNT_NON_POSITIVEamount ≤ 0.
400amount below minimum 1amount is below the server-configured minimum.
400INSUFFICIENT_BALANCE — spot available < amount.
400UNSUPPORTED_TOKEN — token other than DF requested.
403API Key permission denied — caller authenticated via API Key.
503SIGNER_UNAVAILABLE — backend signer or its KMS is unreachable. Transient — retry with backoff.
503spot subsystem disabled — server has SPOT_ENABLED=false.
500internal / sign failed — server error; investigate logs.

Full list: Error Codes.

Code Examples

cURL (JWT)

JWT="your_jwt_token"

curl -s -X POST "https://api-sepolia.p99.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-sepolia.p99.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.