Skip to Content
Core ConceptsQuotes & Exchange Rates

Quotes & Exchange Rates

Quotes provide exchange rate information and allow you to lock rates for a specific time period before creating orders.

What are Quotes?

A Quote is a request for exchange rate information that returns:

  • Current exchange rate between currencies
  • Fees (Koywe fee, network fee, partner fee)
  • Expiration time (how long the rate is valid)
  • Expected amounts (input and output)

Quote Time-to-Live (TTL)

Short Validity: Quotes are valid for 10-15 seconds only. After expiration, you must request a new quote. Always check the expiresAt or validFor field.

Why so short?

  • Crypto and forex markets are volatile
  • Ensures accurate, real-time pricing
  • Protects against price manipulation
  • Prevents stale rate usage

When to Use Quotes

Use quotes when:

  • You need to show customers the exact amount they’ll receive
  • Converting between currencies (BALANCE_TRANSFER)
  • Buying or selling crypto (ONRAMP/OFFRAMP)
  • You want rate transparency

Optional but Recommended: Quotes are optional for most order types but highly recommended for transparency and better user experience.


Creating a Quote

Basic Quote Request

async function getQuote(token, orgId, merchantId, quoteData) { const response = await axios.post( `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`, { type: quoteData.type, // Order type originCurrencySymbol: quoteData.from, // Source currency destinationCurrencySymbol: quoteData.to, // Target currency amountIn: quoteData.amount // Amount to convert }, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } } ); return response.data; } // Usage - Get quote for BALANCE_TRANSFER const quote = await getQuote(token, orgId, merchantId, { type: 'BALANCE_TRANSFER', from: 'COP', to: 'USD', amount: 1000000 // 1,000,000 COP }); console.log('Exchange rate:', quote.exchangeRate); console.log('Will receive:', quote.amountOut, 'USD'); console.log('Valid for:', quote.validFor, 'seconds');
def get_quote(token, org_id, merchant_id, quote_data): response = requests.post( f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/quotes', json={ 'type': quote_data['type'], 'originCurrencySymbol': quote_data['from'], 'destinationCurrencySymbol': quote_data['to'], 'amountIn': quote_data['amount'] }, headers={ 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } ) return response.json() # Usage quote = get_quote(token, org_id, merchant_id, { 'type': 'BALANCE_TRANSFER', 'from': 'COP', 'to': 'USD', 'amount': 1000000 }) print(f"Exchange rate: {quote['exchangeRate']}") print(f"Will receive: {quote['amountOut']} USD")
curl -X POST 'https://api-sandbox.koywe.com/api/v1/organizations/YOUR_ORG_ID/merchants/YOUR_MERCHANT_ID/quotes' \ -H 'Authorization: Bearer YOUR_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "type": "BALANCE_TRANSFER", "originCurrencySymbol": "COP", "destinationCurrencySymbol": "USD", "amountIn": 1000000 }'

Response:

{ "id": "qte_abc123xyz", "type": "BALANCE_TRANSFER", "originCurrencySymbol": "COP", "destinationCurrencySymbol": "USD", "amountIn": 1000000, "amountOut": 250, "exchangeRate": 4000, "koyweFee": 5000, "networkFee": 0, "partnerFee": 0, "validFor": 300, "validUntil": 1731513600, "createdAt": "2025-11-13T15:00:00Z" }

Quote Response Fields

FieldTypeDescription
idstringUnique quote identifier (use in orders)
typestringOrder type (PAYIN, BALANCE_TRANSFER, etc.)
originCurrencySymbolstringSource currency
destinationCurrencySymbolstringTarget currency
amountInnumberInput amount
amountOutnumberOutput amount (what you’ll receive)
exchangeRatenumberExchange rate applied
koyweFeenumberKoywe’s fee (in origin currency)
networkFeenumberNetwork fee for crypto (in origin currency)
partnerFeenumberPartner fee if applicable
validFornumberSeconds until expiration
validUntilnumberUnix timestamp of expiration

Using Quotes in Orders

Rate Locking

When you create an order with a quoteId, the exchange rate is locked:

async function createOrderWithQuote(token, orgId, merchantId) { // 1. Get quote const quote = await axios.post( `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`, { type: 'BALANCE_TRANSFER', originCurrencySymbol: 'COP', destinationCurrencySymbol: 'USD', amountIn: 1000000 }, { headers: { 'Authorization': `Bearer ${token}` } } ); console.log('Rate locked at:', quote.data.exchangeRate); console.log('Valid for:', quote.data.validFor, 'seconds'); // 2. Create order with quote (must be within validFor period) const order = await axios.post( `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`, { type: 'BALANCE_TRANSFER', originCurrencySymbol: 'COP', destinationCurrencySymbol: 'USD', amountIn: 1000000, quoteId: quote.data.id // Lock the rate from quote }, { headers: { 'Authorization': `Bearer ${token}` } } ); console.log('Order created with locked rate'); return order.data; }

Quote Expiration: Quotes are valid for a limited time (10-15 seconds). If the quote expires before you create the order, you’ll need to get a new quote.


Quote Types by Order Type

PAYIN Quotes

For accepting payments (typically same currency, so rate = 1):

{ "type": "PAYIN", "originCurrencySymbol": "COP", "destinationCurrencySymbol": "COP", "amountIn": 50000 }

Response:

{ "exchangeRate": 1, "amountOut": 50000, "koyweFee": 500, "networkFee": 0 }

BALANCE_TRANSFER Quotes

For currency conversion:

{ "type": "BALANCE_TRANSFER", "originCurrencySymbol": "COP", "destinationCurrencySymbol": "USD", "amountIn": 1000000 }

Response:

{ "exchangeRate": 4000, // 4,000 COP = 1 USD "amountOut": 250, // Will receive 250 USD "koyweFee": 5000 // 5,000 COP fee }

ONRAMP Quotes

For buying crypto:

Network Required: ONRAMP quotes require a network parameter to specify the blockchain network for the crypto asset.

{ "type": "ONRAMP", "originCurrencySymbol": "COP", "destinationCurrencySymbol": "USDC", "amountIn": 50000, "network": "ETHEREUM" // Required: blockchain network }

Supported networks:

  • ETHEREUM (for USDC, USDT, ETH)
  • POLYGON (for USDC, USDT, MATIC)
  • BSC (for USDC, USDT, BNB)
  • BITCOIN (for BTC)
  • TRON (for USDT)

Response:

{ "exchangeRate": 4050, // Includes crypto price "amountOut": 12.34, // Will receive 12.34 USDC "koyweFee": 500, "networkFee": 2000, // Blockchain network fee "network": "ETHEREUM" }

OFFRAMP Quotes

For selling crypto:

Network Required: OFFRAMP quotes require a network parameter to specify the blockchain network for the crypto asset.

{ "type": "OFFRAMP", "originCurrencySymbol": "USDC", "destinationCurrencySymbol": "COP", "amountIn": 10, // 10 USDC "network": "ETHEREUM" // Required: blockchain network }

Response:

{ "exchangeRate": 3950, // Slightly lower than buy rate (spread) "amountOut": 39500, // Will receive 39,500 COP "koyweFee": 500, "network": "ETHEREUM" }

Understanding Fees

Fee Breakdown

Example calculation:

Amount In: 1,000,000 COP - Koywe Fee: -5,000 COP - Network Fee: 0 COP - Partner Fee: 0 COP = Net Amount: 995,000 COP ÷ Exchange Rate: ÷4,000 COP/USD = Amount Out: 248.75 USD

Fee Types

Fee TypeWhen AppliedTypical Range
Koywe FeeAll transactions0.5% - 2%
Network FeeCrypto transactions (ONRAMP/OFFRAMP)Variable (blockchain fees)
Partner FeeIf using partner integrationsVaries by partner

Showing Rates to Users

User-Friendly Display

async function displayQuoteToUser(token, orgId, merchantId, amount) { const quote = await getQuote(token, orgId, merchantId, { type: 'BALANCE_TRANSFER', from: 'COP', to: 'USD', amount: amount }); // Format for user display const display = { amountToSend: `${amount.toLocaleString()} COP`, willReceive: `${quote.amountOut.toFixed(2)} USD`, exchangeRate: `1 USD = ${quote.exchangeRate.toLocaleString()} COP`, totalFees: `${quote.koyweFee.toLocaleString()} COP`, expiresIn: `${Math.floor(quote.validFor / 60)} minutes`, effectiveRate: (amount / quote.amountOut).toFixed(2) + ' COP/USD' }; console.log('You send:', display.amountToSend); console.log('They receive:', display.willReceive); console.log('Exchange rate:', display.exchangeRate); console.log('Total fees:', display.totalFees); console.log('Rate expires in:', display.expiresIn); console.log('Effective rate:', display.effectiveRate); return quote; } // Usage await displayQuoteToUser(token, orgId, merchantId, 1000000);

Output:

You send: 1,000,000 COP They receive: 248.75 USD Exchange rate: 1 USD = 4,000 COP Total fees: 5,000 COP Rate expires in: 5 minutes Effective rate: 4020.00 COP/USD

Retrieving a Quote

Get details of an existing quote:

async function getQuoteById(token, orgId, merchantId, quoteId) { const response = await axios.get( `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes/${quoteId}`, { headers: { 'Authorization': `Bearer ${token}` } } ); return response.data; } // Usage const quote = await getQuoteById(token, orgId, merchantId, 'qte_abc123'); console.log('Quote status:', quote);

Best Practices

When to Get a Quote

Always get a quote for:

  • Currency conversions (BALANCE_TRANSFER)
  • Crypto operations (ONRAMP/OFFRAMP)
  • Showing rate to users before they commit

Rate Display

Show users:

  • Exchange rate in familiar terms (1 USD = X COP)
  • Total fees clearly separated
  • Effective rate (including fees)
  • Expiration time

Handling Expiration

async function createOrderWithAutoRetry(token, orgId, merchantId, orderData) { let quote = await getQuote(token, orgId, merchantId, { type: orderData.type, from: orderData.from, to: orderData.to, amount: orderData.amount }); try { // Try to create order with quote return await createOrderWithQuote(token, orgId, merchantId, quote.id); } catch (error) { if (error.message.includes('expired') || error.message.includes('invalid quote')) { // Quote expired, get new quote and retry console.log('Quote expired, getting new quote...'); quote = await getQuote(token, orgId, merchantId, { type: orderData.type, from: orderData.from, to: orderData.to, amount: orderData.amount }); return await createOrderWithQuote(token, orgId, merchantId, quote.id); } throw error; } }

Quote vs No Quote

With Quote

// 1. Get quote const quote = await getQuote(...); // 2. Show user the rate console.log('Rate:', quote.exchangeRate); // 3. User confirms // 4. Create order with locked rate const order = await createOrder({ ...orderData, quoteId: quote.id });

Benefits:

  • Rate locked
  • User knows exactly what they’ll receive
  • Better transparency

Without Quote

// Create order directly (uses current rate) const order = await createOrder({ type: 'BALANCE_TRANSFER', originCurrencySymbol: 'COP', destinationCurrencySymbol: 'USD', amountIn: 1000000 // No quoteId - uses current rate });

Considerations:

  • Rate determined at order creation time
  • Small rate fluctuation possible
  • Faster (one less API call)

Common Scenarios

Scenario 1: Show Rate Before Payment

// User views checkout page const quote = await getQuote(token, orgId, merchantId, { type: 'PAYIN', from: 'COP', to: 'COP', amount: 50000 }); // Display to user console.log(`Pay ${quote.amountIn} COP`); console.log(`Fee: ${quote.koyweFee} COP`); console.log(`Total: ${quote.amountIn + quote.koyweFee} COP`); // User clicks "Pay" const order = await createOrder({ ...orderData, quoteId: quote.id });

Scenario 2: Currency Converter Tool

// Real-time currency converter async function convertCurrency(amount, from, to) { const quote = await getQuote(token, orgId, merchantId, { type: 'BALANCE_TRANSFER', from: from, to: to, amount: amount }); return { from: `${amount} ${from}`, to: `${quote.amountOut} ${to}`, rate: `1 ${to} = ${quote.exchangeRate} ${from}`, fee: `${quote.koyweFee} ${from}` }; } // Usage const result = await convertCurrency(1000000, 'COP', 'USD'); console.log(result); // { from: "1000000 COP", to: "248.75 USD", rate: "1 USD = 4000 COP", fee: "5000 COP" }

Next Steps

Last updated on