Escrows Lifecycle
The Escrows API provides a secure way to hold funds in escrow for business-to-business (B2B) transactions. It supports charging customers, holding funds, and disbursing payouts or refunds as needed.
This guide walks you through the entire lifecycle of a HoneyCoin escrow transaction. The process is asynchronous and event-driven. You will make API calls to initiate actions, and then listen for webhooks to receive the results and determine the next step.
How Status Changes Work: Transactions Drive Escrows
An Escrow's status is directly controlled by the status of its associated Transactions.
- Initiating a charge creates an
escrow-deposittransaction. - Initiating a payout creates an
escrow-payouttransaction. - Initiating a refund creates an
escrow-refundtransaction.
When one of these transactions changes state (e.g., from pending to successful), it automatically updates the parent Escrow's status. This update triggers the escrow_updated webhook, which is your signal to proceed or retry.
Step 1: Create the Escrow ➡️
First, create the escrow transaction. This defines the sender, receiver, and amount. The escrow is created with an initial status of pending_charge.
Action: POST /api/production/v1/escrows
Request Body:
{
"amount": 100,
"chargeCurrency": "KES",
"receiverCurrency": "KES",
"externalReference": "test1f3256",
"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"
}
}
}
The API response will contain the id for this escrow. Save this id. You'll receive your first escrow_updated webhook confirming the pending_charge status.
Step 2: Fund the Escrow 💰
Using the escrowId from Step 1, initiate the charge to collect funds from the sender.
Action: POST /charges/{escrowId}
This call simply starts the process. The escrow status will change to charge_initiated.
Step 3: Listen for the Charge Result Webhook 👂
The escorw-deposit transaction will eventually settle as successful or failed. This settlement updates the escrow's status and triggers a webhook.
Action: Check the status field in the escrow_updated webhook payload.
{
"event": "escrow_updated",
"data": {
"id": "escrow-id-123",
"publicKey": "test-key",
"status": "charge_successful", // \<-- Caused by the deposit transaction succeeding.
"externalReference": "order-ABC-456"
}
}
{
"event": "escrow_updated",
"data": {
"id": "escrow-id-123",
"publicKey": "test-key",
"status": "charge_failed", // \<-- Caused by the deposit transaction failing.
"externalReference": "order-ABC-456"
}
}
If the deposit transaction fails, you'll receive a webhook where the status is charge_failed and you can retry Step 2. Once the status is charge_successful, you can disburse the funds.
Step 4: Disburse the Funds ✅
With the funds held securely, you can now trigger a disbursement—either a payout to the receiver or a refund to the sender.
Path A: Payout to the Receiver
Action: POST /payouts/{escrowId}
To pay out a partial amount, include it in the body. For a full payout, send an empty body.
{}
{
"amount": 50.0
}
Path B: Refund to the Sender ↩️
Action: POST /refunds/{escrowId}
{
"amount": 30.0
}
Step 5: Listen for Disbursement Results & Check the Balance 👂
Each disbursement transaction will trigger an escrow.updated webhook. Crucially, you must check both the status and the balance to know the state of the escrow.
Action: Inspect the status and balance from the webhook payload.
{
"event": "escrow_updated",
"data": {
"id": "escrow-id-123",
"publicKey": "test-key",
"status": "partial_payout_successful",
"balance": 73.50, // \<-- The remaining balance is greater than zero.
"externalReference": "order-ABC-456"
}
}
Step 6: Repeat or Conclude the Flow 🔁
Now, decide what to do next based on the escrow's balance.
- If the
balanceis greater than zero: The escrow is still active. You can return to Step 4 to initiate another payout or refund. - If the balance is zero: The escrow is fully depleted, and its lifecycle is complete. The final status will typically be
successfulorrefunded.
Step 7: (Optional) Formally Close the Escrow 🚪
Closing an escrow is a final, irreversible action that formally marks its lifecycle as complete.
Prerequisites:
Before you can close an escrow, two conditions must be met:
- The escrow balance must be zero.
- All associated transactions must be settled (i.e., none can have a pending status).
Action: POST /escrows/{id}/close
Upon success, the escrow status will be set to closed, and you will receive one last escrow_updated webhook.
Updated 21 days ago
