Z Zise Developers 简体中文

Accept a price increase and hold the difference: 4-hour window

POST /v1/remittances/{id}/reconfirm scope: remittances:write
On behalf of a member · x-on-behalf-of required Requires x-idempotency-key Moves funds · Hold the additional extra_locked amount in locked and simultaneously increase your prepaid hold
This endpoint moves funds

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.

When execution cost at dispatch rises beyond the user’s slippage ceiling relative to the order snapshot, we stop and ask, rather than silently charging more. This endpoint records the user agreeing to pay the increase.

After success, the order returns to dispatching, with the additional amount held in locked.

Prerequisites

  • Order is needs_reconfirm and within the 4-hour window
  • accept_total exactly matches the current pending-action new_total
  • Member’s available asset balance ≥ extra_locked
  • Reconfirmation rounds remain

Path Parameters

FieldTypeRequiredDescription
id string Required Order ID, with or without the rmt_ prefix.
FieldTypeRequiredDescription
x-on-behalf-of string Required Member on whose behalf the call is made.
x-idempotency-key string Required UUID v4. Additional holds move funds; retries reuse the same key.

Request Body

FieldTypeRequiredDescription
accept_total string Required Total debit confirmed by the user. Fixed-point string with precision equal to the asset’s ledger_scale; must exactly match pending-action new_total. We compare strings, not numeric values.USDT uses 6 decimals: "512.340000"

Response

200New price accepted; order returned to dispatch
{
  "id": "rmt_9c1f0a7e-3b2d-4f81-9a55-1d2e3f4a5b6c",
  "status": "dispatching",
  "locked_amount": "517.463400",
  "new_total": "512.340000"
}
400invalid_request: invalid JSON body or empty accept_total. state_invalid: order outside needs_reconfirm, including concurrent advancement to a terminal state; the 4-hour window has expired; or accept_total differs from our current value. For the latter, retrieve pending-action again and ask the user again, rather than automatically resending. insufficient_balance: the member’s available asset balance cannot cover the additional hold.
404not_found: order not associated with this member.
500⚠ Current implementation: exhausted reconfirmation rounds produce api_error here rather than 400. Do not automatically retry 500 responses. First inspect pending-action: can_accept: false + reject_reason: too_many_rounds identifies this case; cancel or wait for expiration.

Additional Details

accept_total must match byte for byte

It is the total debit shown to the user and must match exactly the current new_total from GET /v1/remittances/{id}/pending-action. A mismatch means another dispatch round changed the price. Charging an old number would debit an amount the user did not authorize. The only valid flow is: retrieve pending-action, display new_total, obtain confirmation, and send that exact string back. Do not calculate or cache it yourself.

4 hours, then automatic refund

The window starts when the order enters needs_reconfirm, not when it was created: it may have spent 23 hours under review before dispatch, and the user is only now asked to decide. After expiration, reconciliation releases and refunds the hold. This endpoint does not unlock funds; two independent release paths would introduce a race over which runs first. The deadline in pending-action is the only countdown source.

The number of rounds is limited

An order has a maximum number of reconfirmations. Once exhausted, can_accept: false and reject_reason: too_many_rounds leave only cancellation or expiration.

⚠ An additional hold really moves money. The member’s available balance must cover extra_locked, which includes slippage reserve and is therefore ≥ extra_charge. Insufficiency is checked before rejection rather than producing a 500.

Emitted Events

Green = successful terminal state · Red = terminal state requiring action · Purple = intermediate state. Open an event for its payload and signature verification details.

Request
curl -X POST 'https://api.zinfra.vip/v1/remittances/{id}/reconfirm' \
  -H 'x-auth-token: Bearer $TOKEN' \
  -H 'x-on-behalf-of: $MEMBER_ID' \
  -H 'x-idempotency-key: $IDEMPOTENCY_KEY' \
  -H 'content-type: application/json' \
  -d '{
    "accept_total": "512.340000"
  }'
const res = await fetch("https://api.zinfra.vip/v1/remittances/{id}/reconfirm", {
  method: "POST",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
    "x-on-behalf-of": "$MEMBER_ID",
    "x-idempotency-key": "$IDEMPOTENCY_KEY",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "accept_total": "512.340000"
  }),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();
import requests

res = requests.post(
    "https://api.zinfra.vip/v1/remittances/{id}/reconfirm",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-on-behalf-of": "$MEMBER_ID",
        "x-idempotency-key": "$IDEMPOTENCY_KEY",
        "content-type": "application/json",
    },
    json={
      "accept_total": "512.340000"
    },
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/remittances/{id}/reconfirm",
    strings.NewReader(`{
  "accept_total": "512.340000"
}`))
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
req.Header.Set("x-idempotency-key", "$IDEMPOTENCY_KEY")
req.Header.Set("content-type", "application/json")
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/remittances/{id}/reconfirm"))
    .header("x-auth-token", "Bearer $TOKEN")
    .header("x-on-behalf-of", "$MEMBER_ID")
    .header("x-idempotency-key", "$IDEMPOTENCY_KEY")
    .header("content-type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
{
  "accept_total": "512.340000"
}
"""))
    .build();
// Use String / BigDecimal for amounts, not double.
$ch = curl_init('https://api.zinfra.vip/v1/remittances/{id}/reconfirm');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'x-auth-token: Bearer $TOKEN',
    'x-on-behalf-of: $MEMBER_ID',
    'x-idempotency-key: $IDEMPOTENCY_KEY',
    'content-type: application/json',
  ],
  CURLOPT_POSTFIELDS => <<<'JSON'
{
  "accept_total": "512.340000"
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
200
{
  "id": "rmt_9c1f0a7e-3b2d-4f81-9a55-1d2e3f4a5b6c",
  "status": "dispatching",
  "locked_amount": "517.463400",
  "new_total": "512.340000"
}