Core Concepts

Quotes & Exchange Rates

Understanding quotes and rate locking

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

1async function getQuote(token, orgId, merchantId, quoteData) {
2 const response = await axios.post(
3 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`,
4 {
5 type: quoteData.type, // Order type
6 originCurrencySymbol: quoteData.from, // Source currency
7 destinationCurrencySymbol: quoteData.to, // Target currency
8 amountIn: quoteData.amount // Amount to convert
9 },
10 {
11 headers: {
12 'Authorization': `Bearer ${token}`,
13 'Content-Type': 'application/json'
14 }
15 }
16 );
17
18 return response.data;
19}
20
21// Usage - Get quote for BALANCE_TRANSFER
22const quote = await getQuote(token, orgId, merchantId, {
23 type: 'BALANCE_TRANSFER',
24 from: 'COP',
25 to: 'USD',
26 amount: 1000000 // 1,000,000 COP
27});
28
29console.log('Exchange rate:', quote.exchangeRate);
30console.log('Will receive:', quote.amountOut, 'USD');
31console.log('Valid for:', quote.validFor, 'seconds');

Response:

1{
2 "id": "qte_abc123xyz",
3 "type": "BALANCE_TRANSFER",
4 "originCurrencySymbol": "COP",
5 "destinationCurrencySymbol": "USD",
6 "amountIn": 1000000,
7 "amountOut": 250,
8 "exchangeRate": 4000,
9 "koyweFee": 5000,
10 "networkFee": 0,
11 "partnerFee": 0,
12 "validFor": 300,
13 "validUntil": 1731513600,
14 "createdAt": "2025-11-13T15:00:00Z"
15}

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:

Node.js
1async function createOrderWithQuote(token, orgId, merchantId) {
2 // 1. Get quote
3 const quote = await axios.post(
4 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`,
5 {
6 type: 'BALANCE_TRANSFER',
7 originCurrencySymbol: 'COP',
8 destinationCurrencySymbol: 'USD',
9 amountIn: 1000000
10 },
11 { headers: { 'Authorization': `Bearer ${token}` } }
12 );
13
14 console.log('Rate locked at:', quote.data.exchangeRate);
15 console.log('Valid for:', quote.data.validFor, 'seconds');
16
17 // 2. Create order with quote (must be within validFor period)
18 const order = await axios.post(
19 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
20 {
21 type: 'BALANCE_TRANSFER',
22 originCurrencySymbol: 'COP',
23 destinationCurrencySymbol: 'USD',
24 amountIn: 1000000,
25 quoteId: quote.data.id // Lock the rate from quote
26 },
27 { headers: { 'Authorization': `Bearer ${token}` } }
28 );
29
30 console.log('Order created with locked rate');
31 return order.data;
32}

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):

1{
2 "type": "PAYIN",
3 "originCurrencySymbol": "COP",
4 "destinationCurrencySymbol": "COP",
5 "amountIn": 50000
6}

Response:

1{
2 "exchangeRate": 1,
3 "amountOut": 50000,
4 "koyweFee": 500,
5 "networkFee": 0
6}

BALANCE_TRANSFER Quotes

For currency conversion:

1{
2 "type": "BALANCE_TRANSFER",
3 "originCurrencySymbol": "COP",
4 "destinationCurrencySymbol": "USD",
5 "amountIn": 1000000
6}

Response:

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

ONRAMP Quotes

For buying crypto:

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

1{
2 "type": "ONRAMP",
3 "originCurrencySymbol": "COP",
4 "destinationCurrencySymbol": "USDC",
5 "amountIn": 50000,
6 "network": "ETHEREUM" // Required: blockchain network
7}

Supported networks:

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

Response:

1{
2 "exchangeRate": 4050, // Includes crypto price
3 "amountOut": 12.34, // Will receive 12.34 USDC
4 "koyweFee": 500,
5 "networkFee": 2000, // Blockchain network fee
6 "network": "ETHEREUM"
7}

OFFRAMP Quotes

For selling crypto:

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

1{
2 "type": "OFFRAMP",
3 "originCurrencySymbol": "USDC",
4 "destinationCurrencySymbol": "COP",
5 "amountIn": 10, // 10 USDC
6 "network": "ETHEREUM" // Required: blockchain network
7}

Response:

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

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

Node.js
1async function displayQuoteToUser(token, orgId, merchantId, amount) {
2 const quote = await getQuote(token, orgId, merchantId, {
3 type: 'BALANCE_TRANSFER',
4 from: 'COP',
5 to: 'USD',
6 amount: amount
7 });
8
9 // Format for user display
10 const display = {
11 amountToSend: `${amount.toLocaleString()} COP`,
12 willReceive: `${quote.amountOut.toFixed(2)} USD`,
13 exchangeRate: `1 USD = ${quote.exchangeRate.toLocaleString()} COP`,
14 totalFees: `${quote.koyweFee.toLocaleString()} COP`,
15 expiresIn: `${Math.floor(quote.validFor / 60)} minutes`,
16 effectiveRate: (amount / quote.amountOut).toFixed(2) + ' COP/USD'
17 };
18
19 console.log('You send:', display.amountToSend);
20 console.log('They receive:', display.willReceive);
21 console.log('Exchange rate:', display.exchangeRate);
22 console.log('Total fees:', display.totalFees);
23 console.log('Rate expires in:', display.expiresIn);
24 console.log('Effective rate:', display.effectiveRate);
25
26 return quote;
27}
28
29// Usage
30await 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:

Node.js
1async function getQuoteById(token, orgId, merchantId, quoteId) {
2 const response = await axios.get(
3 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes/${quoteId}`,
4 {
5 headers: { 'Authorization': `Bearer ${token}` }
6 }
7 );
8
9 return response.data;
10}
11
12// Usage
13const quote = await getQuoteById(token, orgId, merchantId, 'qte_abc123');
14console.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

Node.js
1async function createOrderWithAutoRetry(token, orgId, merchantId, orderData) {
2 let quote = await getQuote(token, orgId, merchantId, {
3 type: orderData.type,
4 from: orderData.from,
5 to: orderData.to,
6 amount: orderData.amount
7 });
8
9 try {
10 // Try to create order with quote
11 return await createOrderWithQuote(token, orgId, merchantId, quote.id);
12 } catch (error) {
13 if (error.message.includes('expired') || error.message.includes('invalid quote')) {
14 // Quote expired, get new quote and retry
15 console.log('Quote expired, getting new quote...');
16 quote = await getQuote(token, orgId, merchantId, {
17 type: orderData.type,
18 from: orderData.from,
19 to: orderData.to,
20 amount: orderData.amount
21 });
22
23 return await createOrderWithQuote(token, orgId, merchantId, quote.id);
24 }
25 throw error;
26 }
27}

Quote vs No Quote

With Quote

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

Benefits:

  • Rate locked
  • User knows exactly what theyโ€™ll receive
  • Better transparency

Without Quote

1// Create order directly (uses current rate)
2const order = await createOrder({
3 type: 'BALANCE_TRANSFER',
4 originCurrencySymbol: 'COP',
5 destinationCurrencySymbol: 'USD',
6 amountIn: 1000000
7 // No quoteId - uses current rate
8});

Considerations:

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

Common Scenarios

Scenario 1: Show Rate Before Payment

1// User views checkout page
2const quote = await getQuote(token, orgId, merchantId, {
3 type: 'PAYIN',
4 from: 'COP',
5 to: 'COP',
6 amount: 50000
7});
8
9// Display to user
10console.log(`Pay ${quote.amountIn} COP`);
11console.log(`Fee: ${quote.koyweFee} COP`);
12console.log(`Total: ${quote.amountIn + quote.koyweFee} COP`);
13
14// User clicks "Pay"
15const order = await createOrder({
16 ...orderData,
17 quoteId: quote.id
18});

Scenario 2: Currency Converter Tool

1// Real-time currency converter
2async function convertCurrency(amount, from, to) {
3 const quote = await getQuote(token, orgId, merchantId, {
4 type: 'BALANCE_TRANSFER',
5 from: from,
6 to: to,
7 amount: amount
8 });
9
10 return {
11 from: `${amount} ${from}`,
12 to: `${quote.amountOut} ${to}`,
13 rate: `1 ${to} = ${quote.exchangeRate} ${from}`,
14 fee: `${quote.koyweFee} ${from}`
15 };
16}
17
18// Usage
19const result = await convertCurrency(1000000, 'COP', 'USD');
20console.log(result);
21// { from: "1000000 COP", to: "248.75 USD", rate: "1 USD = 4000 COP", fee: "5000 COP" }

Next Steps