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
| Field | Type | Description |
|---|---|---|
id | string | Unique quote identifier (use in orders) |
type | string | Order type (PAYIN, BALANCE_TRANSFER, etc.) |
originCurrencySymbol | string | Source currency |
destinationCurrencySymbol | string | Target currency |
amountIn | number | Input amount |
amountOut | number | Output amount (what you’ll receive) |
exchangeRate | number | Exchange rate applied |
koyweFee | number | Koywe’s fee (in origin currency) |
networkFee | number | Network fee for crypto (in origin currency) |
partnerFee | number | Partner fee if applicable |
validFor | number | Seconds until expiration |
validUntil | number | Unix 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 USDFee Types
| Fee Type | When Applied | Typical Range |
|---|---|---|
| Koywe Fee | All transactions | 0.5% - 2% |
| Network Fee | Crypto transactions (ONRAMP/OFFRAMP) | Variable (blockchain fees) |
| Partner Fee | If using partner integrations | Varies 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/USDRetrieving 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" }