Remittance estimate: no order or fund hold; read applied_rate
x-on-behalf-of required
No order, fund hold, database write, or idempotency key. This is read-only calculation, safe to call repeatedly as users enter values.
Choose exactly one direction:
- Supply
payout_amount, what the recipient receives, to calculatecustomer_total. - Supply
source_amount, what the sender spends, to reverse-calculatepayout_amount, ensuring a forward calculation ofcustomer_totalis ≤ that budget. Each step rounds down; rounding the other way would debit more than authorized.
Both or neither amounts produce 400; silently preferring one would make the other appear ignored.
Prerequisites
- This line is enabled for you and its settlement currency is configured
- Source asset enabled and spendable, with a fixed-value anchor
- Payee belongs to this member
Request Headers
| Field | Type | Required | Description |
|---|---|---|---|
x-on-behalf-of |
string | Required | Member on whose behalf the call is made; fees and limits are member-scoped. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
payee_id |
string | Required | Payee ID. Optional pye_ prefix; the remaining value must contain digits only. |
line |
"express" | "pobo" | Optional | Product line: express uses the platform as payer; pobo uses the member’s own upstream subaccount for personal remittance. Defaults to express; unknown values silently fall back to express. |
asset |
string | Optional | Asset to debit. Defaults to USDT. |
payout_amount |
string | Optional | Recipient amount in the destination currency. Mutually exclusive with source_amount. Precision depends on the currency; an extra decimal place is rejected by upstream format validation.GBP uses 2 decimals: "380.00"; JPY uses 0: "50000" |
source_amount |
string | Optional | Maximum amount this member will spend in the source asset. Mutually exclusive with payout_amount. Fixed-point string with precision equal to the asset’s ledger_scale.USDT uses 6 decimals: "500.000000" |
Response
rate_available: false + reason, as described above. Only fee_bps / fee_fixed / source_asset are usable then.{
"rate_available": true,
"line": "express",
"source_asset": "USDT",
"payout_currency": "GBP",
"payout_amount": "380.00",
"source_amount": "",
"payer_name": "",
"customer_total": "499.980000",
"below_min": false,
"min_amount": "10.000000",
"fee": "2.480000",
"fee_bps": 50,
"fee_fixed": "1.000000",
"indicative_rate": "1.2899",
"applied_rate": "1.2743",
"slippage": {
"default_bps": 100,
"min_bps": 50,
"max_bps": 1000
}
}invalid_request: both amounts supplied, neither supplied, nonnumeric payee_id, or invalid JSON.
resource_not_found: payee not associated with this member.
product_not_available: line not enabled for you or source asset unavailable.
invalid_fields: amount cannot be parsed, such as precision beyond ledger scale.Additional Details
Read both: indicative_rate and applied_rate differ
indicative_rate is the upstream’s current indicative price; applied_rate is the rate actually used to calculate the payout, after a conservative adjustment. Using only the former makes your displayed numbers unable to reproduce the payout. Testing on 2026-08-10 measured a 2.37% difference, absent from any fee item. Display applied_rate and retain indicative_rate for review.
Neither is an execution price. That is determined at dispatch within the user’s slippage limit.
Unavailable rates return 200, not 5xx
rate_available: false + reason explains the issue. Fees are still returned because they are locally calculable. Mark the rate unavailable and render the rest; hiding everything suggests the whole feature is broken. The three reason values require different handling:
remit_settlement_unconfigured: our configuration issue; waiting does not help the user.remit_rate_unavailable: temporary upstream unavailability; retrying later is meaningful.remit_amount_too_small, accompanied byamount_too_small: true: changing the amount resolves it. Do not present it as “try again later”.
We determine below_min; do not compare it again yourself
Although you have min_amount, the actual criterion uses the payout grid, which you cannot reproduce without the fee, discount, and anchor rate. A local comparison can block a user entering 10 USDT against a minimum of 10, because round-trip quantization yields customer_total of 9.999115.
⚠ Unrecognized line values silently fall back to express, without error. ⚠ Responses echo line / source_asset / source_amount unchanged. Use them to identify whether the response matches the current input; arrival order need not match request order. For reverse calculation, there is no other discriminator.
curl -X POST 'https://api.zinfra.vip/v1/remit/quotes' \
-H 'x-auth-token: Bearer $TOKEN' \
-H 'x-on-behalf-of: $MEMBER_ID' \
-H 'content-type: application/json' \
-d '{
"payee_id": "pye_1042",
"line": "express",
"asset": "USDT",
"payout_amount": "380.00"
}'const res = await fetch("https://api.zinfra.vip/v1/remit/quotes", {
method: "POST",
headers: {
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"content-type": "application/json",
},
body: JSON.stringify({
"payee_id": "pye_1042",
"line": "express",
"asset": "USDT",
"payout_amount": "380.00"
}),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();import requests
res = requests.post(
"https://api.zinfra.vip/v1/remit/quotes",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"content-type": "application/json",
},
json={
"payee_id": "pye_1042",
"line": "express",
"asset": "USDT",
"payout_amount": "380.00"
},
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/remit/quotes",
strings.NewReader(`{
"payee_id": "pye_1042",
"line": "express",
"asset": "USDT",
"payout_amount": "380.00"
}`))
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
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/remit/quotes"))
.header("x-auth-token", "Bearer $TOKEN")
.header("x-on-behalf-of", "$MEMBER_ID")
.header("content-type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("""
{
"payee_id": "pye_1042",
"line": "express",
"asset": "USDT",
"payout_amount": "380.00"
}
"""))
.build();
// Use String / BigDecimal for amounts, not double.$ch = curl_init('https://api.zinfra.vip/v1/remit/quotes');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-auth-token: Bearer $TOKEN',
'x-on-behalf-of: $MEMBER_ID',
'content-type: application/json',
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"payee_id": "pye_1042",
"line": "express",
"asset": "USDT",
"payout_amount": "380.00"
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
{
"rate_available": true,
"line": "express",
"source_asset": "USDT",
"payout_currency": "GBP",
"payout_amount": "380.00",
"source_amount": "",
"payer_name": "",
"customer_total": "499.980000",
"below_min": false,
"min_amount": "10.000000",
"fee": "2.480000",
"fee_bps": 50,
"fee_fixed": "1.000000",
"indicative_rate": "1.2899",
"applied_rate": "1.2743",
"slippage": {
"default_bps": 100,
"min_bps": 50,
"max_bps": 1000
}
}