> ## Documentation Index
> Fetch the complete documentation index at: https://ramps-docs-sync-20260820.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Quote System

> How exchange rates, pricing, and payment execution work

Quotes are Grid's mechanism for providing locked-in exchange rates, transparent fee calculations, and payment instructions. Understanding quotes is essential for all cross-currency or crypto-involved transactions.

## What is a Quote?

A **quote** locks in:

* An exchange rate between two currencies
* Total fees for the transaction
* Exact amounts to be sent and received
* Payment instructions (if JIT funding is needed)
* An expiration time (typically 1-5 minutes, up to 15 minutes depending on the payment type)

Quotes ensure that your customers know exactly what they'll pay and what the recipient will receive before committing to a transaction.

## When Do You Need a Quote?

<Tabs>
  <Tab title="Quotes Required">
    Use quotes for:

    * **Cross-currency transfers** (USD → EUR, BRL → MXN)
    * **Fiat-to-crypto conversion** (USD → BTC)
    * **Crypto-to-fiat conversion** (BTC → USD)
    * **UMA payments** (always require quotes)
    * **JIT funded payments** (need payment instructions)

    These scenarios involve currency conversion, exchange rate risk, or complex routing.
  </Tab>

  <Tab title="Quotes Optional">
    For same-currency transfers, use simpler endpoints:

    * `POST /transfer-out` - Send from internal to external account (same currency)
    * `POST /transfer-in` - Pull from external to internal account (same currency)

    No quote needed because there's no currency conversion.
  </Tab>
</Tabs>

## Creating a Quote

### Basic Cross-Currency Quote

This is a basic quote for a cross-currency transfer from an internal account to an external account,
which were pre-created as described in the [Account Model](/platform-overview/core-concepts/account-model) section.

```bash theme={null}
curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "sourceType": "ACCOUNT",
      "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"
    },
    "destination": {
      "destinationType": "ACCOUNT",
      "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"
    },
    "lockedCurrencySide": "SENDING",
    "lockedCurrencyAmount": 100000
  }'
```

**Response:**

```json theme={null}
{
  "id": "Quote:019542f5-b3e7-1d02-0000-000000000020",
  "status": "PROCESSING",
  "createdAt": "2025-10-03T15:00:00Z",
  "expiresAt": "2025-10-03T15:05:00Z",
  "source": {
    "sourceType": "ACCOUNT",
    "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"
  },
  "destination": {
    "destinationType": "ACCOUNT",
    "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"
  },
  "sendingCurrency": {
    "code": "USD",
    "name": "United States Dollar",
    "symbol": "$",
    "decimals": 2
  },
  "receivingCurrency": {
    "code": "BTC",
    "name": "Bitcoin",
    "symbol": "₿",
    "decimals": 8
  },
  "totalSendingAmount": 100000,
  "totalReceivingAmount": 828835,
  "exchangeRate": 120048.01920768,
  "feesIncluded": 500,
  "platformFeesIncluded": 0,
  "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000025"
}
```

This quote says: Send \$1,000 USD, recipient receives 0.00828835 BTC, fees are \$5.00, expires in 5 minutes.

### Locked Currency Side

You can lock either the **sending** or **receiving** amount:

<Tabs>
  <Tab title="Lock Sending Amount">
    **Use when:** Customer knows exactly how much they want to send

    ```json theme={null}
    {
      "lockedCurrencySide": "SENDING",
      "lockedCurrencyAmount": 100000
    }
    ```

    Grid calculates what the recipient will receive based on current exchange rates.
  </Tab>

  <Tab title="Lock Receiving Amount">
    **Use when:** Recipient must receive an exact amount (e.g., invoice payment)

    ```json theme={null}
    {
      "lockedCurrencySide": "RECEIVING",
      "lockedCurrencyAmount": 92000
    }
    ```

    Grid calculates how much the sender must send to ensure recipient gets exactly €920.
  </Tab>
</Tabs>

## Funding Models

Grid supports two funding models for quotes:

### Prefunded (From Internal Account)

Source is an existing internal account with available balance:

```json theme={null}
{
  "source": {
    "sourceType": "ACCOUNT",
    "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"
  }
}
```

* Funds are debited immediately when quote is executed
* No payment instructions needed
* Best for: Customers with pre-loaded balances

### Just-In-Time (JIT) Funding

Source is the customer ID or the platform itself — Grid provides payment instructions:

```json theme={null}
{
  "source": {
    "sourceType": "REALTIME_FUNDING",
    "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
    "currency": "USD"
  }
}
```

**Quote response includes payment instructions:**

```json theme={null}
{
  "id": "Quote:...",
  "paymentInstructions": [
    {
      "instructionsNotes": "Please ensure the reference code is included in the payment memo/description field",
      "accountOrWalletInfo": {
        "reference": "UMA-Q12345-REF",
        "accountType": "US_ACCOUNT",
        "accountNumber": "9876543210",
        "routingNumber": "110000000",
        "accountCategory": "CHECKING",
        "bankName": "Chase Bank"
      }
    },
    {
      "accountOrWalletInfo": {
        "accountType": "SOLANA_WALLET",
        "assetType": "USDC",
        "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg"
      }
    }
  ]
}
```

* Customer sends funds to provided account with reference
* Quote executes automatically when Grid receives payment
* Best for: On-demand payments without maintaining balances

## Executing a Quote

For a prefunded quote or one from a pullable external account source, once a quote is created, execute it before it expires:

```bash theme={null}
curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes/Quote:abc123/execute \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H "Content-Type: application/json"
```

**Response:**

```json theme={null}
{
  "id": "Quote:019542f5-b3e7-1d02-0000-000000000050",
  "status": "PROCESSING",
  "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000060",
  "totalSendingAmount": 100000,
  "totalReceivingAmount": 91540,
  "feesIncluded": 500,
  "platformFeesIncluded": 0
}
```

### Strong Customer Authentication (EU Customers)

For customers in regions where Strong Customer Authentication (SCA) is required (e.g., EU), quotes may enter a `PENDING_AUTHORIZATION` status with an `scaChallenge` object. The transfer is not released until the customer authorizes it.

Where the challenge appears depends on how the quote is funded:

* **Prefunded** — `POST /quotes/{quoteId}/execute` returns `PENDING_AUTHORIZATION` with the challenge instead of initiating the transfer. The challenge does not exist until `execute` is called, so the proof always goes on the follow-up authorize; re-calling `execute` returns `409`.
* **Realtime (JIT) funded** — the challenge arrives earlier and `paymentInstructions` are withheld until it is satisfied.

**Quote response with SCA challenge:**

```json theme={null}
{
  "id": "Quote:019542f5-b3e7-1d02-0000-000000000050",
  "status": "PENDING_AUTHORIZATION",
  "scaChallenge": {
    "id": "ScaChallenge:...",
    "expiresAt": "2025-10-03T12:05:00Z",
    "factor": "SMS_OTP",
    "availableFactors": ["SMS_OTP", "PASSKEY"],
    "purpose": "PAYOUT"
  }
}
```

To authorize the quote, call `POST /quotes/{quoteId}/authorize` with the appropriate proof:

```bash theme={null}
curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes/Quote:abc123/authorize \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"code": "123456"}'
```

<Note>
  Multiple authorizations may be required in sequence (e.g., currency conversion + payout). After each authorization, check the quote status — if still `PENDING_AUTHORIZATION`, authorize the next challenge.
</Note>

### Execution Timing

<Tabs>
  <Tab title="Prefunded">
    * Transaction created with status `PENDING`
    * Funds debited from source account immediately
    * Settlement begins right away
  </Tab>

  <Tab title="JIT Funded">
    * Quote waits for payment receipt
    * Once Grid receives payment with correct reference
    * Quote executes automatically
    * Transaction created and settlement begins
  </Tab>
</Tabs>

## Immediate Execution

For **market rate execution** without quote approval, use the `immediatelyExecute` flag:

```bash Immediate quote execution theme={null}
curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes \
  -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {"sourceType": "ACCOUNT", "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"},
    "destination": {"destinationType": "ACCOUNT", "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"},
    "lockedCurrencySide": "SENDING",
    "lockedCurrencyAmount": 100000,
    "immediatelyExecute": true
  }'
```

<Accordion title="Response">
  ```json theme={null}
  {
    "id": "Quote:019542f5-b3e7-1d02-0000-000000000020",
    "status": "COMPLETED",
    "createdAt": "2025-10-03T15:00:00Z",
    "expiresAt": "2025-10-03T15:05:00Z",
    "source": {
      "sourceType": "ACCOUNT",
      "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"
    },
    "destination": {
      "destinationType": "ACCOUNT",
      "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"
    },
    "sendingCurrency": {
      "code": "USD",
      "name": "United States Dollar",
      "symbol": "$",
      "decimals": 2
    },
    "receivingCurrency": {
      "code": "EUR",
      "name": "Euro",
      "symbol": "€",
      "decimals": 2
    },
    "totalSendingAmount": 100000,
    "totalReceivingAmount": 91540,
    "exchangeRate": 1.08695652,
    "feesIncluded": 500,
    "platformFeesIncluded": 0,
    "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000025"
  }
  ```
</Accordion>

* Quote is created and executed in one API call
* Best for: Rewards distribution, micro-payments, time-sensitive transfers

<Info>
  `immediatelyExecute` can only be used for quotes with a source that is either an internal account or has direct pull functionality (e.g., ACH pull with an external account).
</Info>

<Warning>
  Customer doesn't see rate before execution. If you want to lock a quote and confirm fees and exchange rate details before executing the quote, set `immediatelyExecute` to `false` or omit the field.
</Warning>

## Fees

All fees are transparently displayed in the quote response:

```json theme={null}
{
  "id": "Quote:019542f5-b3e7-1d02-0000-000000000020",
  "feesIncluded": 500,
  "platformFeesIncluded": 0,
  ... quote response ...
  "rateDetails": {
    "counterpartyMultiplier": 1.08,
    "counterpartyFixedFee": 10,
    "gridApiMultiplier": 0.925,
    "gridApiFixedFee": 10,
    "gridApiVariableFeeRate": 0.003,
    "gridApiVariableFeeAmount": 300
  }
}
```

**Rate Details Breakdown:**

* **`counterpartyMultiplier`**: Exchange rate from mSATs to receiving currency (1.08 = 1 mSAT = 1.08 cents EUR)
* **`counterpartyFixedFee`**: Fixed fee charged by counterparty (10 cents EUR)
* **`gridApiMultiplier`**: Exchange rate from sending currency to mSATs including variable fees (0.925 = \$1 USD = 0.925 mSATs)
* **`gridApiFixedFee`**: Fixed fee charged by Grid API (10 cents USD)
* **`gridApiVariableFeeRate`**: Variable fee rate as percentage (0.003 = 0.3%)
* **`gridApiVariableFeeAmount`**: Variable fee amount (300 cents USD for \$1,000 transaction)

Fees are deducted from the sending amount, so:

* **Customer sends**: \$1,000
* **Fees**: \$5.00
* **Amount converted**: \$995.00
* **Recipient receives**: €915.40 (1 USD ≈ 0.92 EUR, or exchangeRate: 1.08695652)

<Note>
  Quoted fees may fluctuate between quotes. Some fee components (e.g.
  `counterpartyFixedFee` on outgoing transfers, and `gridApiFixedFee` on
  incoming transfers) are denominated in the receiving currency, so their
  value in the sending currency moves with the FX rate. Always reference the
  fees in the latest quote response — they are locked only for the lifetime
  of that quote.
</Note>

### How the variable fee is applied

The variable fee (`gridApiVariableFeeRate`) is always assessed as a fraction of the **sending** amount. How that plays out depends on which side of the quote you lock (see [Locked Currency Side](#locked-currency-side)).

**Sending amount locked** (`lockedCurrencySide: "SENDING"`) — the fee is taken out of the amount you send, and the recipient receives the remainder:

```text theme={null}
received = send − send × fee
```

Send \$50 at a 10 bps (0.10%, `fee = 0.001`) rate: `received = 50 − 50 × 0.001 = 49.95`.

**Receiving amount locked** (`lockedCurrencySide: "RECEIVING"`) — Grid solves for the send amount that nets the exact received amount *after* the same fee:

```text theme={null}
send = received / (1 − fee)
```

Receive \$50 at 10 bps: `send = 50 / 0.999 = 50.05005005…`.

<Note>
  Receiver-locked quotes divide by `(1 − fee)` rather than adding `received × fee` so that locking the receiving side is never cheaper than locking the sending side at the same rate. Solving `send × (1 − fee) = received` for `send` keeps the effective fee rate identical across both locked sides.
</Note>

For cross-currency quotes the exchange rate applies on top of this: the fee is taken on the sending currency, then the net amount is converted at the quoted rate. The same-currency examples above (for example USDC → USD at 1:1) show the fee in isolation.

## Best Practices

<AccordionGroup>
  <Accordion title="Always show the quote to customers before executing">
    Let customers review the exchange rate, fees, and final amounts before committing:

    ```javascript theme={null}
    // ✅ Good: Show quote details, await confirmation
    const quote = await createQuote(params);
    showQuoteToUser(quote);
    if (await userConfirms()) {
      await executeQuote(quote.id);
    }

    // ❌ Bad: Immediate execution without review (unless micro-payments/rewards)
    await createQuote({...params, immediatelyExecute: true});
    ```
  </Accordion>

  <Accordion title="Handle expiration gracefully">
    Store quote parameters so you can recreate expired quotes:

    ```javascript theme={null}
    const quoteParams = {
      source: {accountId: customerAccount},
      destination: {accountId: recipientAccount},
      lockedCurrencySide: 'SENDING',
      lockedCurrencyAmount: amount
    };

    let quote = await createQuote(quoteParams);

    // Later, if expired...
    try {
      await executeQuote(quote.id);
    } catch (error) {
      if (error.code === 'QUOTE_EXPIRED' || error.code === 'QUOTE_RATE_UNAVAILABLE') {
        quote = await createQuote(quoteParams); // Recreate with fresh rate
        await executeQuote(quote.id);
      }
    }
    ```

    `QUOTE_RATE_UNAVAILABLE` means the quoted rate was refused when the quote was
    executed, rather than the quote's own expiry window elapsing. Nothing was
    exchanged in either case, and the recovery is the same: create a new quote.
  </Accordion>

  <Accordion title="Monitor quote status via webhooks">
    Subscribe to quote-related webhooks:

    ```javascript theme={null}
    app.post('/webhooks/grid', (req, res) => {
      const {data: transaction, type} = req.body;

      if (type.startsWith('OUTGOING_PAYMENT.') && transaction.quoteId) {
        // Quote was executed, transaction created
        updateCustomerUI(transaction);
      }

      res.status(200).send();
    });
    ```
  </Accordion>
</AccordionGroup>
