Withdrawals

Withdrawals move funds from a user's available_balanceto their verified bank account via the payment provider's disbursement rail. Your app should check the wallet balance, ensure a bank account exists, then request a withdrawal on behalf of the user.

Live keys only. Test-mode balances are simulated and can never be paid out — a withdrawal request on a test key returns 400("withdrawals are not available in test mode").

Withdrawal statuses

pending → processing → successful
        ↘ failed (funds returned to available balance)
        ↘ cancelled

Withdrawals are auto-approved and move to processing immediately. If Kashia ever disables auto-approval, a request rests at pending until an operator approves it — treat pending as a valid resting state.

Automatic settlement payouts

Marketplace sellers normally never need to withdraw manually: when an escrow settles, Kashia automatically pays the seller's seller_amount — in full, with no withdrawal fee — to their default verified bank account, and emits payout.successful or payout.failed webhooks (reference prefix PYT); payout.initiatedfires additionally when the provider processes the transfer asynchronously, so don't depend on receiving it. If the payout fails or no verified account is on file, the funds stay in the seller's Kashia wallet and Kashia support can retry once an account is added. The manual withdrawal flow below still applies to merchant wallet balances (marketplace commissions, single-vendor proceeds) and to any funds sitting in a wallet.

Limits & fees

  • Minimum withdrawal: ₦1,000 (100,000 kobo)
  • Maximum withdrawal: ₦5,000,000 (500,000,000 kobo)
  • Withdrawal fee: flat ₦50 (5,000 kobo) by default, deducted from the amount
  • All four values are live configuration — read them any time from GET /api/v1/withdrawals/config (no auth required): { fee_type, fee_value, min_amount, max_amount }
  • Settlement payouts bypass all of this: full amount, zero fee, no min/max

Complete flow

  1. GET /external/users/:userId/wallet — confirm available balance
  2. GET /external/users/:userId/bank-accounts — pick a verified account (or add one first)
  3. POST /external/users/:userId/withdrawals — initiate payout
  4. Listen for withdrawal.initiated, withdrawal.successful, or withdrawal.failed webhooks

Endpoints

POST/api/v1/external/users/:userId/withdrawals

Request a withdrawal (returns 201). Debits available balance and initiates the disbursement with the active payment provider.

Request example

bash
curl -X POST https://vault-api.kashiahq.com/api/v1/external/users/USER_UUID/withdrawals \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 9500000,
    "bank_account_id": "BANK_ACCOUNT_UUID",
    "currency": "NGN"
  }'

Response

json
{
  "success": true,
  "data": {
    "id": "uuid",
    "reference": "WTH-a1b2c3d4",
    "amount": 9500000,
    "fee": 5000,
    "net_amount": 9495000,
    "currency": "NGN",
    "status": "processing",
    "source": "manual",
    "retry_count": 0,
    "bank_account": {
      "bank_name": "Guaranty Trust Bank",
      "account_number": "0123456789",
      "account_name": "JOHN DOE"
    },
    "created_at": "2025-01-15T10:30:00Z"
  }
}
GET/api/v1/external/users/:userId/withdrawals

List withdrawals for a user. Supports status filter and pagination.

Parameters
ParameterTypeRequiredDescription
statusstringNopending, processing, successful, failed, cancelled
pageintegerNo (default: 1)Page number
per_pageintegerNo (default: 20)Items per page
GET/api/v1/external/users/:userId/withdrawals/:withdrawalId

Get withdrawal status and bank account summary.