Before you can create a payment, you request a quote collection: a set of priced quotes for a proposed transfer. With /v3/quotes, the collection contains one quote for each payment rail available for the corridor. Each quote has its own exchange rate and fee breakdown, and all quotes in the collection have the same expiry time. Review the terms and choose a quote before you commit to a payment.
This guide covers:
- Creating a quote collection, quoting by source or destination amount.
- Reading the response and choosing a quote.
- Handling quote expiry.
- Requesting a quote for one payment rail.
- Retrieving a quote collection or individual quote after creation.
For conceptual background, see Quotes and exchange rates.
To follow this guide, you need:
- Access to the Payments Direct API UAT environment.
- A valid OAuth2 access token with the
quote_collection:quote:createscope to create quote collections, and thequote_collection:quote:readandquote:quote:readscopes to retrieve them. See Request an access token. - The source and destination currencies and countries for the proposed payment.
- A
payinCategorythat matches your funding model (PRE_FUNDING,CREDIT_FUNDING, orJIT_FUNDING). See Funding methods. - Identities created with the v3 identity operations, which
/v3/quotesrequires. This version of the API documents only those operations, so you already meet this unless you integrated against an earlier version. See Create and manage identities.
POST /v3/quotes/quote-collection
A quote collection request specifies the corridor and amount you want to price. The response contains one quote for each payment rail available for that corridor, leaving out any that Ripple can't price. Not every payment rail for a corridor is available with /v3/quotes.
| Parameter | Required | Description |
|---|---|---|
quoteAmount | Yes | The amount to quote. |
quoteAmountType | Yes | Whether quoteAmount is the send amount (SOURCE_AMOUNT) or the receive amount (DESTINATION_AMOUNT). |
sourceCurrency | Yes | The currency you are sending (ISO 4217, 3–5 characters). |
destinationCurrency | Yes | The currency the beneficiary receives (ISO 4217, 3–5 characters). For a stablecoin payout, the stablecoin. |
payinCategory | Yes | Your funding model: PRE_FUNDING, CREDIT_FUNDING, or JIT_FUNDING. |
destinationCountry | For a fiat payout | The beneficiary country (ISO 3166-1 alpha-2). Omitting it from a fiat payout request returns USR_017. Omit it for a stablecoin payout. |
sourceCountry | No | The sender country (ISO 3166-1 alpha-2). |
paymentRail | No | Requests a quote for this payment rail only. When omitted, the collection contains a quote for each payment rail available for the corridor. See Payment rail reference. |
For a stablecoin payout, each quote's payment rail is a blockchain network, such as ETHEREUM, and quotes are returned only for the networks enabled for your account. Don't include destinationCountry: with it, no payment rail matches, and the request fails with SYS_017, or with USR_024 if you also sent paymentRail. If you request a network that isn't enabled for your account, the request fails with SYS_017. A payment created from a stablecoin quote records the network in destination.selectedPaymentRail and has no destination.destinationBlockchainNetwork value.
Use SOURCE_AMOUNT when you control how much you want to send. The API calculates how much the beneficiary receives after applying the exchange rate and fees.
In this example, the sender wants to send 1,000 USD to a beneficiary in Germany who receives EUR.
curl -i -X POST \
https://api.test.ripple.com/v3/quotes/quote-collection \
-H "Authorization: Bearer <YOUR_JWT_HERE>" \
-H "Content-Type: application/json" \
-d '{
"quoteAmount": 1000.00,
"quoteAmountType": "SOURCE_AMOUNT",
"sourceCurrency": "USD",
"destinationCurrency": "EUR",
"sourceCountry": "US",
"destinationCountry": "DE",
"payinCategory": "PRE_FUNDING"
}'A successful request returns HTTP 201 with a quote collection. The top-level quoteCollectionId identifies the collection, and the quotes array contains one quote for each payment rail Ripple priced. The example shows two payment rails with different fees, and so different destination amounts.
{
"quoteCollectionId": "11111111-aaaa-2222-bbbb-222222222222",
"quotes": [
{
"quoteId": "7ea3399c-1234-5678-8d8f-d320ea406630",
"quoteStatus": "ACTIVE",
"quoteAmountType": "SOURCE_AMOUNT",
"sourceAmount": 1000.00,
"destinationAmount": 915.95,
"sourceCurrency": "USD",
"destinationCurrency": "EUR",
"destinationCountry": "DE",
"payinCategory": "PRE_FUNDING",
"paymentRail": "SEPA_INSTANT",
"adjustedExchangeRate": {
"adjustedRate": 0.9238
},
"fees": [
{
"totalFee": 8.50,
"feeCurrency": "USD",
"feeBreakdown": [
{
"calculatedFee": 0.50,
"feeName": "Fixed service fee",
"feeDescription": "Fixed service fee for payment rail SEPA_INSTANT.",
"paymentRail": "SEPA_INSTANT"
},
{
"calculatedFee": 8.00,
"feeName": "Variable service fee",
"feeDescription": "Variable service fee for payment rail SEPA_INSTANT.",
"paymentRail": "SEPA_INSTANT"
}
]
}
],
"createdAt": "2025-11-02T18:26:00.000Z",
"expiresAt": "2025-11-02T18:41:00.318Z"
},
{
"quoteId": "9fb4500d-2345-6789-ae9f-e431fb517741",
"quoteStatus": "ACTIVE",
"quoteAmountType": "SOURCE_AMOUNT",
"sourceAmount": 1000.00,
"destinationAmount": 918.95,
"sourceCurrency": "USD",
"destinationCurrency": "EUR",
"destinationCountry": "DE",
"payinCategory": "PRE_FUNDING",
"paymentRail": "SEPA_STANDARD",
"adjustedExchangeRate": {
"adjustedRate": 0.9238
},
"fees": [
{
"totalFee": 5.25,
"feeCurrency": "USD",
"feeBreakdown": [
{
"calculatedFee": 0.25,
"feeName": "Fixed service fee",
"feeDescription": "Fixed service fee for payment rail SEPA_STANDARD.",
"paymentRail": "SEPA_STANDARD"
},
{
"calculatedFee": 5.00,
"feeName": "Variable service fee",
"feeDescription": "Variable service fee for payment rail SEPA_STANDARD.",
"paymentRail": "SEPA_STANDARD"
}
]
}
],
"createdAt": "2025-11-02T18:26:00.041Z",
"expiresAt": "2025-11-02T18:41:00.318Z"
}
]
}| Field | Description |
|---|---|
quoteCollectionId | The ID of this collection. Use it to retrieve the collection later with GET /v3/quotes/quote-collection/{quote-collection-id}. |
quoteId | The ID of an individual quote. Use the quoteId of the quote you choose to create a payment. |
quoteStatus | ACTIVE means the quote can be used. EXPIRED means the validity window has passed and a new quote collection is required. |
paymentRail | The payment rail for this quote, for example SEPA_INSTANT or SPEI. Each quote in the collection is for a different payment rail. |
adjustedExchangeRate.adjustedRate | The FX rate Ripple applies to the payment. The rate is locked during the quote's validity window. |
fees[].totalFee / feeBreakdown | Total service fee and a line-item breakdown. A line item can include the paymentRail it applies to. |
taxes | Applicable consumption taxes on Ripple's fees, such as VAT or GST. Absent when no taxes apply. |
createdAt / expiresAt | The quote's validity window. By default, quotes are valid for 15 minutes. |
Each quote in the collection is for one payment rail and has its own exchange rate and fees. All quotes in the collection expire at the same time. Review the quotes and choose the one that fits the payment. The payment records the payment rail of the quote you choose in destination.selectedPaymentRail. When present, payoutExecutionDetails.paymentRailUsed reports the rail used for the payout, which can differ. For each payment rail's delivery speed, see Payment rail reference.
If Ripple can't price a payment rail for the request, the collection doesn't include a quote for it. If no payment rail can be priced, the request fails, usually with SYS_018, or with USR_013 when the quote amount isn't enough to cover the transaction fees.
The quoteId you choose becomes the paymentId when you create the payment. Store it before moving to the next step.
Quotes are valid for a limited time (15 minutes by default). Before creating a payment, check that the quoteStatus of the quote you chose is ACTIVE.
- If
quoteStatusisACTIVE, the quote can be used to create a payment. - If
quoteStatusisEXPIRED, the quote can no longer be used. Request a new quote collection, which reflects the current exchange rate and fees.
Use expiresAt to implement client-side refresh logic, for example, prompting a user to confirm before the quote lapses, or automatically re-quoting when the remaining time falls below a threshold.
remainingSeconds = expiresAt - now()
if remainingSeconds <= 0:
request a new quote collectionAll quotes in a collection have the same expiresAt. Re-quoting generates a new quoteCollectionId and new quoteId values.
If you already know which payment rail you want to use, include paymentRail in the request to receive a collection with one quote. This simplifies response handling when your integration always uses the same payment rail.
curl -i -X POST \
https://api.test.ripple.com/v3/quotes/quote-collection \
-H "Authorization: Bearer <YOUR_JWT_HERE>" \
-H "Content-Type: application/json" \
-d '{
"quoteAmount": 10000,
"quoteAmountType": "SOURCE_AMOUNT",
"sourceCurrency": "USD",
"destinationCurrency": "MXN",
"sourceCountry": "US",
"destinationCountry": "MX",
"payinCategory": "PRE_FUNDING",
"paymentRail": "SPEI"
}'The response contains a quotes array with one quote, for the SPEI payment rail. If the corridor doesn't offer the payment rail, the request fails with USR_024, and the error description lists the payment rails /v3/quotes offers for the corridor. If the corridor offers the payment rail, but not with /v3/quotes, the request fails with USR_026.
For valid paymentRail values, see Payment rail reference.
GET /v3/quotes/quote-collection/{quote-collection-id}
Use this endpoint to retrieve a quote collection you created earlier, for example in an async flow where you stored the quoteCollectionId and are checking quote status before initiating a payment.
curl -i -X GET \
https://api.test.ripple.com/v3/quotes/quote-collection/11111111-aaaa-2222-bbbb-222222222222 \
-H "Authorization: Bearer <YOUR_JWT_HERE>"The response schema is identical to the POST response. Check the quoteStatus of the quote you want to use before proceeding, because it may have expired since it was created.
GET /v3/quotes/{quote-id}
Use this endpoint to retrieve a specific quote by ID.
curl -i -X GET \
https://api.test.ripple.com/v3/quotes/7ea3399c-1234-5678-8d8f-d320ea406630 \
-H "Authorization: Bearer <YOUR_JWT_HERE>"The response contains the same fields as a single entry from the quotes array, without the quoteCollectionId wrapper.
Quote errors return a structured response with a status and an errors array. Each error includes code, title, type, description, and timestamp.
Error codes use three prefixes:
| Prefix | Category | Common causes |
|---|---|---|
USR_ | Validation error | Missing required fields, invalid currency or country codes, an amount outside the allowed range, a paymentRail that isn't available for the corridor, the deprecated payinCategory values, or a corridor that isn't available with /v3/quotes. |
CFG_ | Configuration error | Missing or incomplete configuration for your account. Contact your Ripple representative if the error persists. |
SYS_ | System error | An internal or upstream failure. This includes SYS_018 when no payment rail could be priced, and SYS_017 when /v3/quotes finds no payment rail for the request: the corridor isn't available with /v3/quotes, or, for a stablecoin payout, no requested network is enabled for your account. Retrying doesn't change a SYS_017 result; for a fiat corridor, quote through /v2/quotes, documented in Payments Direct API v2026.03. For other system errors, retry the request and contact support if the error persists. |
Deprecated payinCategory values (USR_025): /v3/quotes doesn't accept FUNDED or T_PLUS_ONE. Use PRE_FUNDING in place of FUNDED, and CREDIT_FUNDING in place of T_PLUS_ONE.
Payment rail not offered (USR_024): The corridor doesn't offer the paymentRail you sent. The error description lists the payment rails /v3/quotes offers for the corridor.
Not available with /v3/quotes (USR_026): If you sent paymentRail, retry without it, because a payment rail that isn't available with /v3/quotes returns USR_026 even when other payment rails for the corridor are. Otherwise, quote this corridor through /v2/quotes, documented in Payments Direct API v2026.03.
Missing destinationCountry (USR_017): A fiat payout request must include destinationCountry. A stablecoin payout request must not include it.
Invalid currency or country codes: sourceCurrency and destinationCurrency must be 3–5 alphabetical characters. sourceCountry and destinationCountry must be ISO 3166-1 alpha-2 codes (exactly 2 characters).
For every error code, see API errors.
After you have an ACTIVE quote and have chosen a quoteId, you are ready to create a payment.
See Create a payment for a complete walkthrough of the payment creation step, including how to pass the quoteId alongside beneficiary identity and financial instrument IDs.