Escrows
Overview
This guide walks you through the full lifecycle of a Cashless escrow transaction and seller balance management. The process is asynchronous and event-driven.
Core Concepts
An Escrow's status is controlled by the status of its associated Transactions:
- Initiating a charge creates an escrow-deposit transaction.
- Initiating a payout creates an escrow-payout transaction.
- Initiating a refund creates an escrow-refund transaction.
When a transaction changes state, it automatically updates the parent Escrow's status and triggers the escrow_updated webhook.
Seller Balance System
If an escrow includes a sellerId, seller balances track automatically:
- Charge successful: Seller balance increases by the escrow amount
- Payout successful: Seller balance decreases by the payout amount
- Refund successful: Seller balance decreases by the refund amount
Escrow Transaction Flow
Step 1: Create the Escrow
POST /api/production/v1/escrows
{
"amount": 100,
"chargeCurrency": "KES",
"receiverCurrency": "KES",
"externalReference": "test1f3256",
"sellerId": "seller-uuid-here",
"chargeDetails": {
"method": "momo",
"firstName": "Billy",
"lastName": "Batson",
"country": "KE",
"email": "[email protected]",
"phoneNumber": "254712345678",
"momoOperatorId": "mpesa"
},
"payoutDetails": {
"method": "momo",
"country": "KE",
"payoutMethod": {
"accountName": "Virgil Hawkins",
"accountNumber": "254712345678"
}
}
}Save the id from the response — this is your escrowId.
Step 2: Fund the Escrow
POST /charges/{escrowId} — initiates the charge. Status changes to charge_initiated.
Step 3: Listen for Charge Result
Watch for the escrow_updated webhook. If status is charge_successful, funds are held and you can proceed to disburse.
Step 4: Disburse Funds
Payout: POST /payouts/{escrowId} — full or partial payout, optionally override payout details.
Refund: POST /refunds/{escrowId} — full or partial refund to the sender.
Step 5: Monitor Disbursement & Balance
Check both status and balance in the escrow_updated webhook to know if the escrow still has funds.
Step 6: Repeat or Conclude
balance > 0: Escrow still active — you can initiate another payout or refund.balance = 0: Lifecycle complete.
Step 7: (Optional) Close the Escrow
POST /escrows/{id}/close — irreversible. Requires zero balance and all transactions settled.
Escrow Status Values
pending_charge→charge_initiated→charge_successful/charge_failedpayout_initiated→payout_successful/payout_failedrefund_initiated→refund_successful/refund_failedclosed
Seller Balance Management
Create a Seller
POST /api/production/v1/escrows/sellers
{
"name": "John's Coffee Shop",
"email": "[email protected]",
"currency": "KES",
"defaultPayoutDetails": {
"method": "momo",
"country": "KE",
"payoutMethod": {
"accountName": "John Doe",
"accountNumber": "254712345678"
}
}
}Sellers can hold multi-currency balances:
{
"aggregatedBalance": {
"KES": 1250.50,
"USD": 75.25,
"NGN": 45000.00
}
}Query Seller
GET /api/production/v1/escrows/sellers/{sellerId}— details + balanceGET /api/production/v1/escrows/sellers— list all sellersGET /api/production/v1/escrows/sellers/{sellerId}/transactions— transaction history
Webhook: escrow_updated
{
"event": "escrow_updated",
"data": {
"id": "escrow-id-123",
"publicKey": "user-public-key",
"status": "charge_successful",
"balance": 100.0,
"externalReference": "order-ABC-456"
}
}Updated about 2 months ago
