Withdrawals
A withdrawal sends your available balance to one of your own approved payout wallets (cash-out).
| Method | Path | |
|---|---|---|
| POST | /v1/withdrawals | Create a withdrawal |
| GET | /v1/withdrawals/:id | Retrieve a withdrawal |
To list withdrawals, use GET /v1/transactions?type=WITHDRAWAL; add approval_status=PENDING to see those waiting for a second approver.
The withdrawal object
{
"id": "wdl_01J9Z6C3D4E5F6G7H8J9K0M1N2",
"object": "withdrawal",
"status": "INITIATED",
"amount": "6000000",
"currency": "UGX",
"country": "UG",
"network": "MTN",
"destination": "wal_01J9Z3K8Q4W6XK2M7N5P0R3T8V",
"msisdn_masked": "+2567****111",
"reference": "sweep-2026-09-28",
"fee": "7500",
"total_debited": "6007500",
"provider": null,
"approval_status": "PENDING",
"failure_code": null,
"failure_message": null,
"metadata": null,
"created_at": "2026-09-28T17:00:00.000Z",
"updated_at": "2026-09-28T17:00:00.000Z"
}Attributes
idstring- Unique id, prefixed
wdl_. objectstring- Always
"withdrawal". statusenumINITIATEDwhile waiting for approval; then as in statuses.amountstring- What your wallet receives, in full.
currencyenumUGX,KESorGHS.countryenum- The wallet's country:
UG,KEorGH. networkenumMTNorAIRTEL.destinationstring- The payout wallet id (
wal_…). msisdn_maskedstring- The wallet number, masked (
+2567****456). Full numbers are never returned. referencestring- Your reference, unchanged.
feestring- The payout fee, charged on top. Shown from the start (it is the amount held).
total_debitedstringamount + fee: held when requested, paid out on success, released on failure.providerstring | null- An identifier for the rail that carried the payment. Informational: don't build logic on it.
approval_statusenumNOT_REQUIRED,PENDING(waiting for a second team member),APPROVEDorREJECTED.failure_codestring | null- As for other transactions, plus
approval_rejectedwhen a team member rejects it. failure_messagestring | null- Human-readable reason. Don't parse it.
metadataobject | null- As sent.
created_atstring- ISO 8601, UTC.
updated_atstring- ISO 8601, UTC. Changes with the status.
Create a withdrawal
POST/v1/withdrawals
The wallet must be ACTIVE (approved and past its 24-hour cooling-off), and currency must be its country's currency. Above your approval threshold it waits for a second team member. Rate-limited to 60 per minute.
Headers
Idempotency-KeyheaderRequired- 8–255 characters. Same key and body returns the original response. See Idempotency.
Body parameters
amountstringRequired- Integer minor units as a string, e.g.
"50000". See Amounts. currencyenumRequiredUGX,KESorGHS. Must be the currency of the wallet's country.destinationstringRequired- A payout wallet id (
wal_…) fromGET /v1/payout_destinations. referencestringRequired- Your own id for this payment, e.g. your order id. 1–128 characters from
A–Z a–z 0–9 . _ : -. Returned unchanged everywhere. narrationstringOptional- Up to 100 characters. Shown to the recipient where the network supports it.
metadataobjectOptional- Up to 20 keys of your own data. Values are strings (≤ 512 characters), numbers, booleans or
null.
curl -X POST https://api.skanpay.website/v1/withdrawals \
-H "Authorization: Bearer $SKANPAY_SECRET_KEY" \
-H "Idempotency-Key: sweep-2026-09-28" \
-H "Content-Type: application/json" \
-d '{"amount":"2500000","currency":"UGX","destination":"wal_01J9Z3K8Q4W6XK2M7N5P0R3T8V","reference":"sweep-2026-09-28"}'Errors specific to withdrawals: destination_not_found, destination_not_active, insufficient_balance, withdrawals_not_enabled_live.
Retrieve a withdrawal
GET/v1/withdrawals/:id
curl https://api.skanpay.website/v1/withdrawals/wdl_01J9Z6C3D4E5F6G7H8J9K0M1N2 \
-H "Authorization: Bearer $SKANPAY_SECRET_KEY"Returns the withdrawal, or 404 withdrawal_not_found.