Accept a price increase and hold the difference: 4-hour window
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
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
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Required | Order ID, with or without the rmt_ prefix. |
Request Headers
| Field | Type | Required | Description |
|---|---|---|---|
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
| Field | Type | Required | Description |
|---|---|---|---|
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
{
"id": "rmt_9c1f0a7e-3b2d-4f81-9a55-1d2e3f4a5b6c",
"status": "dispatching",
"locked_amount": "517.463400",
"new_total": "512.340000"
}invalid_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.not_found: order not associated with this member.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.
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.
{
"id": "rmt_9c1f0a7e-3b2d-4f81-9a55-1d2e3f4a5b6c",
"status": "dispatching",
"locked_amount": "517.463400",
"new_total": "512.340000"
}