FX Behavior & Rate Locking
Cashela provides transparent foreign exchange (FX) rates for all cross-currency transactions. This guide explains how rates are calculated, locked, and applied.
Overview
When processing cross-currency payments, Cashela:
- Quotes real-time rates from wholesale FX markets
- Applies transparent spreads disclosed in each quotation
- Locks rates for a defined period (typically 1 hour)
- Discloses all fees upfront before transaction confirmation
FX Rate Calculation
Rate Components
| Component | Description |
|---|---|
wholesale_fx_rate | Base market rate from wholesale FX providers |
applied_spread | Cashela’s spread percentage (disclosed) |
final_rate | Rate applied to transaction (wholesale + spread) |
Example Calculation
Source: 1,000 USDDestination: BRL
Wholesale Rate: 5.4200Applied Spread: 0.5%Final Rate: 5.4200 × (1 - 0.005) = 5.3929
Destination Amount: 1,000 × 5.3929 = 5,392.90 BRLRate Locking
How It Works
- Request a quotation – Receive locked rate valid for a specific period
- Review terms – Display rate and fees to your user
- Execute within validity – Create transaction before expiration
- Rate guaranteed – Transaction uses locked rate regardless of market movement
Quotation Response
{ "success": true, "data": { "id": "qtn_01HJ3KBCD8E9F0G1H2I3J4K5L6", "source": { "amount": 1000.00, "currency": "USD" }, "destination": { "amount": 5392.90, "currency": "BRL" }, "fx_rate": { "wholesale_rate": 5.4200, "applied_rate": 5.3929, "spread_percentage": 0.50 }, "fee": { "amount": 5.00, "currency": "USD" }, "total_cost": { "amount": 1005.00, "currency": "USD" }, "expiration_date": "2024-01-15T11:30:00.000Z", "valid_for_seconds": 3600 }}Lock Duration
| Product | Typical Lock Duration |
|---|---|
| Pay-In | A deposit keeps the quote it was created with until it is paid or its payment window (validity_period) ends. Rates returned by Get exchange rates are indicative and not locked. |
| Pay-Out quotation | 30 minutes. See Quotation rate locking. |
Fee Structure
Fee Types
| Fee Type | Description |
|---|---|
transaction_fee | Fixed or percentage fee per transaction |
fx_spread | Built into the exchange rate |
network_fee | Fees from payment networks (if applicable) |
Fee Display
Always display fees clearly to your users:
function displayPricingBreakdown(quotation) { const breakdown = { sourceAmount: `${quotation.source.amount} ${quotation.source.currency}`, exchangeRate: `1 ${quotation.source.currency} = ${quotation.fx_rate.applied_rate} ${quotation.destination.currency}`, transactionFee: `${quotation.fee.amount} ${quotation.fee.currency}`, totalCost: `${quotation.total_cost.amount} ${quotation.total_cost.currency}`, destinationAmount: `${quotation.destination.amount} ${quotation.destination.currency}`, rateLockedUntil: new Date(quotation.expiration_date).toLocaleString() };
return breakdown;}Handling Rate Expiration
Check Before Transaction
async function createTransactionSafely(quotationId, transactionData) { // 1. Get current quotation status const quotation = await api.getQuotation(quotationId);
// 2. Check if expired const expiresAt = new Date(quotation.expiration_date); const now = new Date();
if (now >= expiresAt) { // Rate expired - need new quotation throw new QuotationExpiredError('Rate lock has expired. Please get a new quote.'); }
// 3. Warn if expiring soon (within 5 minutes) const minutesRemaining = (expiresAt - now) / (1000 * 60); if (minutesRemaining < 5) { console.warn('Quotation expiring soon:', minutesRemaining.toFixed(1), 'minutes'); }
// 4. Create transaction return await api.createTransaction({ quotation_id: quotationId, ...transactionData });}Auto-Refresh Pattern
async function getQuotationWithAutoRefresh(params, minValidMinutes = 10) { let quotation = await api.createQuotation(params);
// If expires in less than threshold, immediately get a fresh quote const expiresAt = new Date(quotation.expiration_date); const now = new Date(); const minutesRemaining = (expiresAt - now) / (1000 * 60);
if (minutesRemaining < minValidMinutes) { // Wait briefly and get a fresh quote await sleep(1000); quotation = await api.createQuotation(params); }
return quotation;}Exchange Rate API
Pay-In: indicative rate
POST
/api/v1/pay-in/deposit-creation/exchange-ratesReturns data.country, data.amount and data.exchange_rates for a country and payment type. The rate is indicative; the amounts that apply to a deposit are the ones in Get payment methods and in the deposit. See Get exchange rates and, for card payments charged in USD, Card payments.
Best Practices
1. Always Show Locked Rates
Display the locked rate and expiration time to users:
<div class="rate-lock-info"> <p>Exchange Rate: 1 USD = 5.3929 BRL</p> <p>Rate locked until: 11:30 AM (in 45 minutes)</p> <p class="warning" id="expiry-warning" style="display:none;"> ⚠️ Rate expires soon. Complete your transfer now. </p></div>
<script> // Show warning when < 10 minutes remaining setInterval(() => { const remaining = (expiryDate - new Date()) / 60000; if (remaining < 10 && remaining > 0) { document.getElementById('expiry-warning').style.display = 'block'; } }, 30000);</script>2. Handle Rate Changes Gracefully
When a quotation expires mid-flow:
try { await createTransaction(quotationId, data);} catch (error) { if (error.code === 'QUOTATION_EXPIRED') { // Get new quote and show updated rate to user const newQuotation = await createQuotation(params);
if (hasRateChangedSignificantly(oldQuotation, newQuotation)) { // Ask user to confirm new rate const confirmed = await showRateChangeConfirmation(oldQuotation, newQuotation); if (!confirmed) { throw new UserCancelledError(); } }
// Retry with new quotation await createTransaction(newQuotation.id, data); }}3. Log Rate Information
Keep records for reconciliation and compliance:
await db.transactionLogs.create({ transactionId: transaction.id, quotationId: quotation.id, wholesaleRate: quotation.fx_rate.wholesale_rate, appliedRate: quotation.fx_rate.applied_rate, spreadPercentage: quotation.fx_rate.spread_percentage, sourceAmount: quotation.source.amount, destinationAmount: quotation.destination.amount, fee: quotation.fee.amount, timestamp: new Date()});Related Documentation
- PayOut → Create Quotation – Lock rates for payouts
- PayIn → Get Exchange Rates – Current exchange rates
- Error Handling – Handling quotation expiry errors