Settlement Times & Processing Flows
Settlement and processing times vary based on payment method, destination country, payer network, and transaction time. This guide helps you set accurate expectations for your users and manage your treasury operations.
PayIn Settlement
When a Pay-In deposit reaches COMPLETED, the net amount (net_amount, in settlement_currency, as reported in the notification) is credited to your Cashela account. Credited funds are released for use according to Cashela’s settlement schedule and your agreement with Cashela.
- Reconcile money only on
COMPLETEDandREVERSEDdeposits.DECLINED,CANCELLEDandFAILEDdeposits move no money. - A full void or refund of a card deposit takes the principal plus the Cashela fee back from your balance. See Voids and refunds.
- How long your customer takes to pay depends on the payment method; card payments must be completed within the checkout’s
validity_period(about one hour).
PayOut Settlement
Overview
PayOut settlement refers to when funds are delivered to the beneficiary’s account or available for pickup.
Settlement by Delivery Method
| Delivery Method | Typical Settlement | Cutoff Impact |
|---|---|---|
| Bank Transfer (ACH) | 1 – 2 business days | Next day if before cutoff |
| Bank Transfer (Instant) | Minutes to 4 hours | 24/7 availability |
| PIX (Brazil) | Instant | 24/7 availability |
| SPEI (Mexico) | Minutes to 2 hours | Extended hours (6AM-11PM) |
| Cash Pickup | 15 min – 4 hours | Partner location hours |
| Mobile Wallet | Minutes to 1 hour | 24/7 availability |
| SWIFT | 2 – 5 business days | Banking hours only |
Processing Cutoff Times
Many payers have daily cutoff times. Transactions submitted after cutoff are processed the next business day.
function getExpectedDelivery(payer, submissionTime) { const cutoffTime = payer.cut_off_time; // e.g., "16:00" const timezone = payer.cut_off_timezone; // e.g., "America/Sao_Paulo"
// Convert submission time to payer's timezone const localTime = submissionTime.toLocaleString('en-US', { timeZone: timezone }); const [cutoffHour, cutoffMinute] = cutoffTime.split(':').map(Number);
// Check if after cutoff const localHour = new Date(localTime).getHours(); const isAfterCutoff = localHour >= cutoffHour;
// Calculate expected delivery let deliveryDate = new Date(submissionTime);
if (isAfterCutoff) { // Add one business day deliveryDate = addBusinessDays(deliveryDate, 1, timezone); }
// Add payer's typical processing time const minHours = payer.estimated_delivery.minimum_hours; const maxHours = payer.estimated_delivery.maximum_hours;
return { earliest: new Date(deliveryDate.getTime() + minHours * 3600000), latest: new Date(deliveryDate.getTime() + maxHours * 3600000), isAfterCutoff };}Real-Time Processing
Processing Flow
All transactions go through a real-time processing pipeline:
┌─────────────┐ ┌──────────────┐ ┌────────────────┐│ Transaction │────►│ Validation & │────►│ Provider/Payer ││ Created │ │ Compliance │ │ Submission │└─────────────┘ └──────────────┘ └───────┬────────┘ │ ▼┌─────────────┐ ┌──────────────┐ ┌────────────────┐│ Webhook │◄────│ Status │◄────│ Provider ││ Delivered │ │ Update │ │ Processing │└─────────────┘ └──────────────┘ └────────────────┘Status Updates
Receive real-time status updates via webhooks:
| Status | Description | Webhook Event |
|---|---|---|
CREATED | Transaction initialized | transaction.created |
PROCESSING | Being processed | transaction.processing |
SUBMITTED | Sent to provider | transaction.submitted |
COMPLETED | Successfully delivered | transaction.completed |
FAILED | Processing failed | transaction.failed |
Business Day Considerations
Holidays and Weekends
Processing times are affected by:
- Local bank holidays – Destination country holidays
- Weekends – Most bank transfers pause
- Provider maintenance – Scheduled downtime
Handling Non-Business Days
function isBusinessDay(date, countryCode) { const holidays = getHolidays(countryCode, date.getFullYear()); const dayOfWeek = date.getDay();
// Weekend check (0 = Sunday, 6 = Saturday) if (dayOfWeek === 0 || dayOfWeek === 6) { return false; }
// Holiday check const dateStr = date.toISOString().split('T')[0]; if (holidays.includes(dateStr)) { return false; }
return true;}
function addBusinessDays(date, days, countryCode) { let result = new Date(date); let addedDays = 0;
while (addedDays < days) { result.setDate(result.getDate() + 1); if (isBusinessDay(result, countryCode)) { addedDays++; } }
return result;}Displaying Estimates to Users
Best Practice: Show Range
Always show a range rather than a specific time:
function formatDeliveryEstimate(payer) { const { minimum_hours, maximum_hours } = payer.estimated_delivery;
if (minimum_hours === 0 && maximum_hours === 0) { return 'Instant delivery'; }
if (minimum_hours === 0) { return `Up to ${formatDuration(maximum_hours)}`; }
return `${formatDuration(minimum_hours)} to ${formatDuration(maximum_hours)}`;}
function formatDuration(hours) { if (hours < 1) { return `${Math.round(hours * 60)} minutes`; } if (hours < 24) { return `${hours} hour${hours > 1 ? 's' : ''}`; } const days = Math.round(hours / 24); return `${days} business day${days > 1 ? 's' : ''}`;}
// Output examples:// "Instant delivery"// "Up to 4 hours"// "1 to 2 business days"Reconciliation
Daily Reconciliation Process
async function dailyReconciliation() { // 1. Get all transactions from the previous day const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1);
const transactions = await api.getTransactions({ created_after: startOfDay(yesterday), created_before: endOfDay(yesterday) });
// 2. Group by status const byStatus = groupBy(transactions, 'status');
// 3. Check for stuck transactions const processing = byStatus['PROCESSING'] || []; const stuckTransactions = processing.filter(tx => { const age = Date.now() - new Date(tx.created_at).getTime(); const maxAge = tx.payer.estimated_delivery.maximum_hours * 3600000; return age > maxAge * 1.5; // 50% over expected time });
if (stuckTransactions.length > 0) { alertOperations('Stuck transactions detected', stuckTransactions); }
// 4. Generate report return { total: transactions.length, completed: (byStatus['COMPLETED'] || []).length, failed: (byStatus['FAILED'] || []).length, pending: processing.length, stuck: stuckTransactions.length };}Best Practices
- Set accurate expectations – Show realistic delivery times based on payer data
- Account for cutoffs – Warn users about approaching cutoff times
- Monitor webhooks – Don’t rely solely on polling for status updates
- Implement reconciliation – Run daily checks to catch stuck transactions
- Handle time zones – Display times in user’s local timezone
- Plan for holidays – Warn users during holiday periods
Related Documentation
- PayIn → Webhooks – PayIn status notifications
- PayOut → Transaction – Transaction status tracking
- Notifications – Webhook event handling
- Get Payers – Delivery time estimates by payer