Guides
FX quotes and rates
Before creating a cross-border payment, you'll typically want to check the exchange rate and get a quote that shows the payer exactly how much they'll pay. Nexpay provides two endpoints for this: the FX Rate API for quick rate lookups, and the Quotes API for detailed quotes with fee breakdowns per settlement method.
Before you start
You will need:
- A valid API key to authenticate your requests.
- A payee
id(for quotes) — use the Payees API to create or retrieve one.
Checking the exchange rate
To get the current exchange rate between two currencies, use the FX Rate API. This is useful for displaying indicative rates before the payer commits to a payment.
curl -X POST 'https://api.nexpay.com.au/v2/fx-rate' \
-H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret' \
-H 'Content-Type: application/json' \
-d '{
"fromCurrency": "AUD",
"toCurrency": "USD",
"amount": 1,
"fromCountryCode": "AUS",
"toCountryCode": "USA",
"paymentType": "provider"
}'
try {
const response = await fetch('https://api.nexpay.com.au/v2/fx-rate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
},
body: JSON.stringify({
"fromCurrency": "AUD",
"toCurrency": "USD",
"amount": 1,
"fromCountryCode": "AUS",
"toCountryCode": "USA",
"paymentType": "provider"
}),
});
if (response.ok) {
const { data } = await response.json();
console.log(data);
} else {
throw new Error(`Request failed with status: ${response.status}`);
}
} catch (error) {
console.error(error);
}
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->post('https://api.nexpay.com.au/v2/fx-rate', [
'fromCurrency' => 'AUD',
'toCurrency' => 'USD',
'amount' => 1,
'fromCountryCode' => 'AUS',
'toCountryCode' => 'USA',
'paymentType' => 'provider',
]);
$data = $response->json('data');
echo $data['rate'];
The response includes the current rate for the corridor:
{
"data": {
"fromCurrencyCode": "AUD",
"toCurrencyCode": "USD",
"rate": 0.7031508029966657,
"taxPercentage": 0,
"settlementMethod": 5,
"expiresOn": "2026-06-02T18:36:29.516Z"
}
}
Country codes are ISO 3166-1 alpha-3
On the FX Rate endpoint, fromCountryCode and toCountryCode are 3-letter country codes (AUS, USA, BRA) — different from the 2-letter countryCode used on the Quotes endpoint and on payment-intent payloads. See Currencies & countries.
FX Rate request fields
| Field | Type | Required | Description |
|---|---|---|---|
fromCurrency | string | Yes | 3-letter ISO 4217 source currency code (e.g. AUD). |
toCurrency | string | Yes | 3-letter ISO 4217 destination currency code (e.g. USD). |
amount | number | Yes | Amount to convert, in decimal form. |
fromCountryCode | string | Yes | 3-letter ISO 3166-1 alpha-3 source country code (e.g. AUS). |
toCountryCode | string | Yes | 3-letter ISO 3166-1 alpha-3 destination country code (e.g. USA). |
paymentType | string | Yes | The type of payment: provider, company, or private. |
Creating a quote
While the FX Rate API gives you a quick rate check, the Quotes API provides a full breakdown with fees and multiple settlement method options — one per available payment rail (bank transfer, card, installment, Pix, etc.). Use quotes when you're ready to show the payer their final costs and to commit a quoteId to a payment.
The Quotes endpoint accepts a payouts array so you can quote multiple recipients in a single call (used by split and batch flows). For a single-recipient quote, send one item:
Multi-payout quotes need the split-payment entitlement
A quote with more than one payouts entry (any tenant split or batch) requires the AllowSplitPayment permission on your user / API key — it maps to the multiPayoutQuote payment-access capability. Single-payout quotes do not.
Without it, POST /v2/quotes returns 403 [GEN9004]:
{
"statusCode": 403,
"code": "[GEN9004]",
"message": "[GEN9004] Forbidden",
"details": {
"reason": "Payment access does not allow this quote creation",
"legacyConstraint": "payment_access.multiPayoutQuote",
"failedRequirements": ["User.AllowSplitPayment"]
}
}
Grant AllowSplitPayment in the Dashboard (or ask your Nexpay account manager) before going live with split payments. The change is synced to your API access asynchronously — allow a short propagation delay.
curl -X POST 'https://api.nexpay.com.au/v2/quotes' \
-H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret' \
-H 'Content-Type: application/json' \
-d '{
"payouts": [
{ "payeeId": 12141, "amount": 1000 }
],
"countryCode": "AU",
"paymentType": "provider"
}'
try {
const response = await fetch('https://api.nexpay.com.au/v2/quotes', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
},
body: JSON.stringify({
"payouts": [
{ "payeeId": 12141, "amount": 1000 }
],
"countryCode": "AU",
"paymentType": "provider"
}),
});
if (response.ok) {
const { data: quote } = await response.json();
console.log('Quote ID:', quote.quoteId);
console.log('Expires on:', quote.expiresOn);
console.log('Variants:', quote.variants.length);
} else {
throw new Error(`Request failed with status: ${response.status}`);
}
} catch (error) {
console.error(error);
}
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->post('https://api.nexpay.com.au/v2/quotes', [
'payouts' => [
['payeeId' => 12141, 'amount' => 1000],
],
'countryCode' => 'AU',
'paymentType' => 'provider',
]);
$quote = $response->json('data');
echo 'Quote ID: ' . $quote['quoteId'];
echo 'Expires on: ' . $quote['expiresOn'];
echo 'Variants: ' . count($quote['variants']);
The response wraps the quote in the standard data envelope and includes an array of variants — one per available settlement method — so the payer can compare options:
{
"data": {
"quoteId": "902b1624-d2d4-46e5-b087-bfcb311fb128",
"countryCode": "AU",
"paymentType": "provider",
"expiresOn": "2026-06-03T08:30:00.000Z",
"hasInstallments": false,
"variants": [
{
"id": 1175,
"settlementMethod": "dmt",
"settlementChannel": "bank",
"fromCurrency": "AUD",
"toCurrency": "AUD",
"fromAmount": 1005,
"fxRate": 1.005,
"fee": 10,
"minAmount": 100,
"maxAmount": 50000,
"spread": 0.005,
"marketRate": 1,
"commissionBeneficiaryId": 51,
"commission": 0.5,
"meta": {
"category": "bank-transfer",
"uiFlow": "standard",
"eta": { "disbursement": { "unit": "days", "min": 1, "max": 3 } },
"noticeKeys": ["notice.dmt.proof_required"]
}
}
],
"payouts": [
{
"payeeId": 12141,
"fromCurrency": "AUD",
"toCurrency": "AUD",
"payerAmount": 1000,
"payeeAmount": 1000,
"fxRate": 1
}
]
}
}
Quote request fields
| Field | Type | Required | Description |
|---|---|---|---|
payouts | array | Yes | One or more { payeeId, amount } pairs. amount is the amount each payee will receive, in decimal form. The payee's currency is inferred from the payee record. |
countryCode | string | Yes (Provider/Private) | 2-letter ISO 3166-1 alpha-2 code of the country the payer is paying FROM. Company quotes derive this server-side from the linked-school bank, so it may be omitted on company quotes. |
paymentType | string | Yes | The type of payment: provider, company, or private. (Note: payments themselves call this field transactionType.) |
Payment types
| Type | Description |
|---|---|
provider | Provider/institution payments (e.g. university tuition, school fees). |
company | Business-to-business payments. |
private | Individual/personal transfers (e.g. family remittances). |
Understanding quote variants
Each variant in the response represents a different way the payer can send money — bank transfer (dmt), card capture, Pix, BPAY, installments, and so on. To commit a quote to a payment intent, send the chosen variant's id in instructions.manualPayment.selectedQuoteVariantId — it is a server-assigned numeric id, NOT an array index.
Key variant fields:
| Field | Description |
|---|---|
id | Server-assigned numeric id of this variant. Use this value as selectedQuoteVariantId when creating a payment intent. |
settlementMethod | The payment rail (e.g. dmt, card, installment, pix). |
settlementChannel | The processing channel (e.g. bank, checkout, volt). |
fromCurrency / toCurrency | The currency the payer sends and the payee receives. |
fromAmount | Total the payer pays in fromCurrency (already includes fee). |
fxRate | The exchange rate used for this variant (payer-facing rate, includes spread). |
fee | The fixed fee for this settlement method, in fromCurrency. |
minAmount / maxAmount | Limits enforced by this rail, in fromCurrency. |
marketRate / spread | The mid-market rate and the spread applied to derive fxRate. Display-only. |
meta | Connector-specific metadata: category (bank-transfer, card, cash, wallet), uiFlow hint, ETA, card networks, etc. Use for rendering the payment-method picker. |
The top-level payouts array mirrors the per-recipient settlement amounts in payee currency:
payerAmount— what the payer pays toward this recipient (per the selected variant)payeeAmount— what the recipient receivesfxRate— the corridor rate for this leg
Retrieving a quote
You can retrieve a previously created quote by its quoteId:
curl 'https://api.nexpay.com.au/v2/quotes/902b1624-d2d4-46e5-b087-bfcb311fb128' \
-H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret'
try {
const response = await fetch(
'https://api.nexpay.com.au/v2/quotes/902b1624-d2d4-46e5-b087-bfcb311fb128',
{
method: 'GET',
headers: { 'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret' },
},
);
if (response.ok) {
const { data: quote } = await response.json();
console.log(quote);
} else {
throw new Error(`Request failed with status: ${response.status}`);
}
} catch (error) {
console.error(error);
}
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->get('https://api.nexpay.com.au/v2/quotes/902b1624-d2d4-46e5-b087-bfcb311fb128');
$quote = $response->json('data');
print_r($quote);
Things to know
- Quotes have an expiry time (
expiresOn). Always check this before submitting a payment — using an expired quote returns a[QOT0001]error from/submit(HTTP422). - Different settlement methods may have different rates and fees. Present all variants to the payer so they can choose the best option.
- The
amountin the quote request is the amount the payee receives (destination amount), not what the payer sends. - If the currency pair is unavailable, the API returns a
[QOT0003]error. Use the FX Rate API to check supported pairs first. - FX rates fluctuate — always create a fresh quote close to when the payer is ready to pay.
- The Quotes endpoint uses
paymentType; the FX Rate endpoint uses the same value spelledtransactionType. This is a known inconsistency; the values themselves are interchangeable.