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-deposit transaction.
  • Initiating a payout creates an escrow-payout transaction.
  • Initiating a refund creates an escrow-refund transaction.

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 balance is 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 successful or refunded.

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.