Confirm after the merchant completes the on-chain payout
x-idempotency-key
Moves funds · Clears the member's corresponding withdrawing amount and reduces merchant.custody equally.
See the response table below for failure handling. Retry timeouts (504) with the same idempotency key — we may already have processed the request; use a new key after a business failure; the same key replays that failure.
Clears the corresponding withdrawing amount and reduces merchant.custody equally, since you owe the member less. This is final, with no undo endpoint. Call only after the on-chain transaction has actually succeeded.
Call as the merchant; no x-on-behalf-of is required, since the member is read from the order.
Concurrent and repeated calls
State claiming happens in the database, with a CHECK violation rolling back the entire batch, rather than a separate read-then-update. Retries are therefore safe:
- Already
settled: 200 +replayed: true, without posting again. - Already
failed: 400order_not_cancellable, meaning the other path won first. QueryGET /v1/withdrawals/{id}for current status. - Two simultaneous requests: only one posts; the other receives a replay.
⚠ merchant_custody_shortfall is distinct from insufficient balance. It means confirming this payout would make your custody balance for this asset negative: your declared deposits total less than the payouts you are confirming. The remedy is not replenishing prepayment; declare the missing deposits first.
Prerequisites
- The order is currently locked; settled orders replay idempotently, while failed orders are rejected.
- Your custody balance for this asset on our ledger is sufficient; otherwise returns merchant_custody_shortfall.
Path Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Required | Withdrawal order ID, with or without the wdr_ prefix. |
Request Headers
| Field | Type | Required | Description |
|---|---|---|---|
x-idempotency-key |
string | Required | UUID。 |
Response
replayed: true means no new posting occurred on this call.{
"id": "wdr_9f1c0b2a-4d33-4a51-9f2e-7c1b0a5d6e88",
"status": "settled",
"replayed": false
}order_not_cancellable: the order has moved to another state, usually failed.
merchant_custody_shortfall: insufficient custody balance; declare missing deposits first.not_found: nonexistent or belonging to another merchant.idempotency_key_reused · idempotency_in_progresscurl -X POST 'https://api.zinfra.vip/v1/withdrawals/{id}/confirm' \
-H 'x-auth-token: Bearer $TOKEN' \
-H 'x-idempotency-key: $IDEMPOTENCY_KEY'const res = await fetch("https://api.zinfra.vip/v1/withdrawals/{id}/confirm", {
method: "POST",
headers: {
"x-auth-token": "Bearer $TOKEN",
"x-idempotency-key": "$IDEMPOTENCY_KEY",
},
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();import requests
res = requests.post(
"https://api.zinfra.vip/v1/withdrawals/{id}/confirm",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-idempotency-key": "$IDEMPOTENCY_KEY",
},
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/withdrawals/{id}/confirm",
nil)
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-idempotency-key", "$IDEMPOTENCY_KEY")
res, err := http.DefaultClient.Do(req)
// Decode amount fields as string, not float64.HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.zinfra.vip/v1/withdrawals/{id}/confirm"))
.header("x-auth-token", "Bearer $TOKEN")
.header("x-idempotency-key", "$IDEMPOTENCY_KEY")
.method("POST", HttpRequest.BodyPublishers.noBody())
.build();
// Use String / BigDecimal for amounts, not double.$ch = curl_init('https://api.zinfra.vip/v1/withdrawals/{id}/confirm');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-auth-token: Bearer $TOKEN',
'x-idempotency-key: $IDEMPOTENCY_KEY',
],
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
{
"id": "wdr_9f1c0b2a-4d33-4a51-9f2e-7c1b0a5d6e88",
"status": "settled",
"replayed": false
}