Payout Flow Diagram
This guide illustrates the complete PayOut flow from quotation to completion, including all API calls, validations, and webhook events.
Complete Integration Flow
┌─────────────────────────────────────────────────────────────────────────────┐│ PAYOUT INTEGRATION FLOW │└─────────────────────────────────────────────────────────────────────────────┘
┌─────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐│ Your │ │ Cashela │ │ Payer │ │ Your Server ││ Client │ │ API │ │ Network │ │ (Webhooks) │└────┬────┘ └──────┬──────┘ └──────┬──────┘ └────────┬────────┘ │ │ │ │ │ 1. Get Countries/Services │ │ │─────────────────► │ │ │ ◄─────────────── [BRA, MEX, COL] │ │ │ │ │ │ │ 2. Get Available Payers │ │ │─────────────────► │ │ │ ◄─────────────── [Banks, Wallets] │ │ │ │ │ │ │ 3. Create Quotation │ │ │─────────────────► │ │ │ ◄─────────────── Locked Rate + Fees│ │ │ (quotation_id, expires_at) │ │ │ │ │ │ │ 4. Collect Beneficiary Details │ │ │ (Based on payer requirements) │ │ │ │ │ │ │ 5. Create Transaction │ │ │─────────────────► │ │ │ ◄─────────────── Transaction Created│ │ │ │ │ │ │ │ 6. Submit to Payer │ │ │─────────────────► │ │ │ │ │ │ │ │ 7. Payer Confirms │ │ │ │◄──────────────────│ │ │ │ │ │ │ │ 8. Webhook Notification │ │ │───────────────────────────────────────►│ │ │ │ │ │ │ 9. Acknowledge (200 OK) │ │ │◄───────────────────────────────────────│ │ │ │ │ │ 10. Get Transaction Status │ │ │─────────────────► │ │ │ ◄─────────────── COMPLETED │ │ │ │ │ │Step-by-Step Process
Step 1: Get Countries & Services
Retrieve supported destination countries and available payout services.
GET /api/v1/pay-out/countriesGET /api/v1/pay-out/servicesPurpose: Determine where you can send money and through which channels.
Step 2: Get Available Payers
Fetch the list of banks, wallets, and cash pickup locations for the destination.
GET /api/v1/pay-out/payers?country_iso_code=BRA¤cy=BRLPurpose: Show the beneficiary their delivery options with requirements.
Step 3: Create Quotation
Lock in the exchange rate and calculate fees before the user commits.
POST /api/v1/pay-out/quotationPurpose: Display the exact amount the beneficiary will receive with all fees disclosed.
Important: The quotation is valid for a limited time (typically 1 hour). Complete the transaction before expiration.
Step 4: Collect Beneficiary Details
Based on the selected payer, collect required information:
| Delivery Method | Required Fields |
|---|---|
| Bank Transfer | Account number, branch code, account type |
| Mobile Wallet | Mobile number, wallet type |
| Cash Pickup | ID type, ID number, pickup location |
Step 5: Create Transaction
Submit the payout request with all sender and beneficiary details.
POST /api/v1/pay-out/transactionPurpose: Initiate the money transfer using the locked quotation.
Steps 6-9: Processing & Webhooks
The payout is processed through the payer network. Your server receives webhook notifications for status changes.
Step 10: Verify Transaction Status
Optionally query the transaction status for confirmation.
GET /api/v1/pay-out/transaction/{id}State Machine
┌─────────────┐ │ CREATED │ └──────┬──────┘ │ ┌──────▼──────┐ │ PROCESSING │ └──────┬──────┘ │ ┌──────▼──────┐ │ SUBMITTED │ └──────┬──────┘ │ ┌─────────────────┼─────────────────┐ │ │ │ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │ COMPLETED │ │ FAILED │ │ CANCELLED │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ ┌──────▼──────┐ ┌──────▼──────┐ │ (Final) │ │ REFUNDED │ └─────────────┘ └─────────────┘| Status | Description | Terminal |
|---|---|---|
CREATED | Transaction created, being validated | No |
PROCESSING | Submitted to internal processing | No |
SUBMITTED | Sent to payer network | No |
COMPLETED | Funds delivered to beneficiary | Yes |
FAILED | Transaction failed (see reason) | Yes |
CANCELLED | Transaction was cancelled | Yes |
REFUNDED | Funds returned to your account | Yes |
Quotation Expiration Handling
Quotations have a limited validity window. Implement proper handling:
async function createPayoutWithQuotation(payoutData) { // 1. Create quotation const quotation = await api.createQuotation({ source_currency: 'USD', destination_currency: 'BRL', amount: payoutData.amount, payer_id: payoutData.payerId });
// 2. Check if quotation will expire soon const expiresAt = new Date(quotation.expiration_date); const now = new Date(); const minutesRemaining = (expiresAt - now) / (1000 * 60);
if (minutesRemaining < 5) { // Refresh quotation if expiring soon return createPayoutWithQuotation(payoutData); }
// 3. Display quotation details to user await displayQuotationToUser(quotation);
// 4. User confirms - create transaction const transaction = await api.createTransaction({ quotation_id: quotation.id, ...payoutData });
return transaction;}Integration Patterns
Pattern 1: Form-Based Flow
Best for: Step-by-step wizard interfaces
// Step 1: Select destinationconst countries = await api.getCountries();const selectedCountry = await showCountrySelector(countries);
// Step 2: Select payerconst payers = await api.getPayers({ country_iso_code: selectedCountry });const selectedPayer = await showPayerSelector(payers);
// Step 3: Enter amount and get quoteconst quotation = await api.createQuotation({ destination_country: selectedCountry, payer_id: selectedPayer.id, source_amount: amount});await showQuotationReview(quotation);
// Step 4: Collect beneficiary detailsconst beneficiaryFields = selectedPayer.required_receiving_entity_fields;const beneficiaryData = await collectBeneficiaryData(beneficiaryFields);
// Step 5: Submit transactionconst transaction = await api.createTransaction({ quotation_id: quotation.id, beneficiary: beneficiaryData, sender: senderData});Pattern 2: Saved Beneficiary Flow
Best for: Repeat transfers to same beneficiary
// User has saved beneficiariesconst savedBeneficiaries = await db.getSavedBeneficiaries(userId);
// Select beneficiaryconst beneficiary = await showBeneficiarySelector(savedBeneficiaries);
// Create quotation with known payerconst quotation = await api.createQuotation({ payer_id: beneficiary.payer_id, source_amount: amount});
// Direct to confirmation - skip data collectionconst transaction = await api.createTransaction({ quotation_id: quotation.id, beneficiary: beneficiary.details, sender: senderData, external_identifier: `repeat_${beneficiary.id}_${Date.now()}`});Error Handling
Common Errors and Resolutions
| Error | Cause | Resolution |
|---|---|---|
QUOTATION_EXPIRED | Rate lock expired | Create new quotation |
INSUFFICIENT_FUNDS | Low account balance | Top up balance |
INVALID_ACCOUNT | Wrong account number | Verify beneficiary details |
PAYER_UNAVAILABLE | Payer temporarily down | Try alternative payer |
COMPLIANCE_HOLD | Requires review | Contact support |
Retry Strategy
function isRetryable(error) { const retryableCodes = [ 'RATE_LIMITED', 'INTERNAL_ERROR', 'PAYER_TIMEOUT', 'SERVICE_UNAVAILABLE' ]; return retryableCodes.includes(error.code);}
async function createTransactionWithRetry(data, maxRetries = 3) { const idempotencyKey = crypto.randomUUID();
for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await api.createTransaction(data, { idempotencyKey }); } catch (error) { if (error.code === 'QUOTATION_EXPIRED') { // Need new quotation - cannot retry throw error; }
if (isRetryable(error) && attempt < maxRetries) { const delay = Math.pow(2, attempt) * 1000; await sleep(delay); continue; }
throw error; } }}Best Practices Checklist
- Always use quotations – Never assume exchange rates
- Validate beneficiary data – Check format before submitting
- Handle quotation expiry – Refresh if close to expiration
- Use idempotency keys – Prevent duplicate transactions
- Implement webhook handlers – Process all status updates
- Store transaction references – Keep both your ID and Cashela’s
- Log all API calls – Include request_id for support
- Test with sandbox – Verify all corridors before production
Related Documentation
- Create Quotation – Lock in exchange rates
- Create Transaction – Execute the payout
- Get Payers – Query available payers
- Countries – Supported destinations
- Webhooks – Event notifications
- Error Handling – Error codes and handling