# Fetch a quote collection

Before you can create a payment, you request a **quote collection**: a set of priced quotes for a proposed transfer. Each quote locks in an exchange rate, fee breakdown, and validity window so you can review the terms before committing to a payment.

This guide covers:

1. Creating a quote collection, quoting by source or destination amount.
2. Reading the response.
3. Handling quote expiry.
4. Retrieving a quote collection or individual quote after creation.


For conceptual background, see [Quotes and exchange rates](/products/payments-direct-2/introduction/concepts/quotes).

## Before you begin

To follow this guide, you need:

- Access to the Payments Direct API UAT environment.
- A valid OAuth2 **access token** with the `quote_collections:write` scope. See [Request an access token](/products/payments-direct-2/api-docs/developer-guides/request-an-access-token).
- The source and destination currencies and countries for the proposed payment.
- A `payinCategory` that matches your funding model (`PRE_FUNDING`, `CREDIT_FUNDING`, or `JIT_FUNDING`). See [Funding methods](/products/payments-direct-2/introduction/concepts/funding-methods).
- A `payoutCategory` of `BANK` or `CRYPTO`.


## Step 1: Create a quote collection

`POST /v2/quotes/quote-collection`

A quote collection request specifies the corridor, payout category, and amount you want to price. A quote collection typically contains a single quote.

### Request parameters

| 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). |
| `payinCategory` | Yes | Your funding model: `PRE_FUNDING`, `CREDIT_FUNDING`, or `JIT_FUNDING`. |
| `payoutCategory` | Yes | The payout type: `BANK` for bank account payouts, `CRYPTO` for digital asset payouts. |
| `sourceCountry` | No | The sender country (ISO 3166-1 alpha-2). |
| `destinationCountry` | No | The beneficiary country (ISO 3166-1 alpha-2). |
| `destinationBlockchainNetwork` | No | Required when `payoutCategory` is `CRYPTO`. Specifies the blockchain network for the payout. |


Quote by source amount
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 **10,000 USD** to a beneficiary in Mexico who receives MXN.


```bash
curl -i -X POST \
  https://api.test.ripple.com/v2/quotes/quote-collection \
  -H "Authorization: Bearer <YOUR_JWT_HERE>" \
  -H "Content-Type: application/json" \
  -d '{
    "quoteAmount": 10000.00,
    "quoteAmountType": "SOURCE_AMOUNT",
    "sourceCurrency": "USD",
    "destinationCurrency": "MXN",
    "sourceCountry": "US",
    "destinationCountry": "MX",
    "payinCategory": "PRE_FUNDING",
    "payoutCategory": "BANK"
  }'
```

Quote by destination amount
Use `DESTINATION_AMOUNT` when the beneficiary must receive a precise amount, for example when paying a foreign-currency invoice. The API calculates how much you need to send to cover the exchange and fees.

In this example, the beneficiary must receive exactly **200,000 MXN**.


```bash
curl -i -X POST \
  https://api.test.ripple.com/v2/quotes/quote-collection \
  -H "Authorization: Bearer <YOUR_JWT_HERE>" \
  -H "Content-Type: application/json" \
  -d '{
    "quoteAmount": 200000.00,
    "quoteAmountType": "DESTINATION_AMOUNT",
    "sourceCurrency": "USD",
    "destinationCurrency": "MXN",
    "sourceCountry": "US",
    "destinationCountry": "MX",
    "payinCategory": "PRE_FUNDING",
    "payoutCategory": "BANK"
  }'
```

## Step 2: Review the response

A successful request returns HTTP `201` with a quote collection. The top-level `quoteCollectionId` identifies the collection; the `quotes` array contains the returned quote.


```json
{
  "quoteCollectionId": "11111111-aaaa-2222-bbbb-222222222222",
  "quotes": [
    {
      "quoteId": "7ea3399c-1234-5678-8d8f-d320ea406630",
      "quoteStatus": "ACTIVE",
      "quoteAmountType": "SOURCE_AMOUNT",
      "sourceAmount": 10000.00,
      "destinationAmount": 204533.30,
      "sourceCurrency": "USD",
      "destinationCurrency": "MXN",
      "sourceCountry": "US",
      "destinationCountry": "MX",
      "payoutCategory": "BANK",
      "payinCategory": "PRE_FUNDING",
      "adjustedExchangeRate": {
        "adjustedRate": 20.4136
      },
      "fees": [
        {
          "totalFee": 14.00,
          "feeCurrency": "USD",
          "feeBreakdown": [
            {
              "calculatedFee": 2.00,
              "feeName": "Fixed service fee",
              "feeDescription": "Fixed service fee for this transaction."
            },
            {
              "calculatedFee": 12.00,
              "feeName": "Variable service fee",
              "feeDescription": "Variable service fee for this transaction."
            }
          ]
        }
      ],
      "createdAt": "2025-11-13T22:44:34.711Z",
      "expiresAt": "2025-11-13T22:59:34.711Z"
    }
  ]
}
```

### Key fields

| Field | Description |
|  --- | --- |
| `quoteCollectionId` | The ID of this collection. Use it to retrieve the collection later with `GET /v2/quotes/quote-collection/{quote-collection-id}`. |
| `quoteId` | The ID of an individual quote. Use the `quoteId` 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. |
| `payoutCategory` | The payout type for this quote (`BANK` or `CRYPTO`). |
| `paymentRail` | When present, the payment rail used for this quote (for example, `SPEI`, `ACH`). |
| `adjustedExchangeRate.adjustedRate` | The FX rate Ripple will apply. This rate is locked for the duration of the quote's validity window. |
| `fees[].totalFee` | Total service fee for the transaction. |
| `taxes` | Applicable consumption taxes on Ripple's fees (VAT, GST, and so on). Absent when no taxes apply. |
| `createdAt` / `expiresAt` | The quote's validity window. By default, quotes are valid for 15 minutes. |


quoteId and paymentId
The `quoteId` becomes the `paymentId` when you create the payment. Store it before moving to the next step.

## Step 3: Handle quote expiry

Quotes are valid for a limited time (15 minutes by default). Before creating a payment, check that `quoteStatus` is `ACTIVE`.

- If `quoteStatus` is `ACTIVE`, the quote can be used to create a payment.
- If `quoteStatus` is `EXPIRED`, the quote can no longer be used. Request a new quote collection, which will reflect 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 collection
```

Re-quoting generates a new `quoteCollectionId` and new `quoteId` values.

## Step 4: Retrieve a quote collection

`GET /v2/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.


```bash
curl -i -X GET \
  https://api.test.ripple.com/v2/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 quote's `quoteStatus` before proceeding; the quote may have expired since it was created.

## Step 5: Retrieve an individual quote

`GET /v2/quotes/{quote-id}`

Use this endpoint to retrieve a specific quote by ID.


```bash
curl -i -X GET \
  https://api.test.ripple.com/v2/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.

## Error handling

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 codes, amounts outside the allowed range, or invalid `payinCategory` or `payoutCategory` values. |
| `CFG_` | Configuration error | Unsupported corridor or missing tenant configuration. |
| `SYS_` | System error | Internal or upstream FX pricing failure. Retry the request; contact support if the error persists. |


### Common validation errors

**Missing `payoutCategory`**: The v2 endpoint requires `payoutCategory`. Omitting it returns a `USR_` validation error.

**Invalid currency or country codes**: `sourceCurrency` and `destinationCurrency` must be 3–5 alphabetic characters. `sourceCountry` and `destinationCountry` must be ISO 3166-1 alpha-2 codes (exactly 2 characters).

**Deprecated `payinCategory` values**: `FUNDED` and `T_PLUS_ONE` are still accepted on the v2 endpoint but are deprecated. Use `PRE_FUNDING` in place of `FUNDED`, and `CREDIT_FUNDING` in place of `T_PLUS_ONE`. See [Funding methods](/products/payments-direct-2/introduction/concepts/funding-methods).

**Unsupported corridor**: If the currency pair is not configured for your tenant, the API returns a `CFG_` error. Contact your Ripple representative if the error persists.

## What's next

Once you have an `ACTIVE` quote and have selected a `quoteId`, you are ready to create a payment.

See [Create a payment](/products/payments-direct-2/api-docs/developer-guides/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.