Withdraw Funds
Request a withdrawal from your wallet balance to a Nigerian bank account.
This endpoint requires an API key with the profile scope. Services-scoped keys receive a 403 Forbidden.
Withdrawals must be enabled per key. New keys are created with withdrawals disabled and return 412 PRECONDITION_FAILED until you enable Allow Withdrawals in the Developer Console (Settings → Developer). This applies to both test and live keys.
All API requests must be made over HTTPS to: https://app.kdadatasub.name.ng
Endpoint
Overview
Withdraw funds from your wallet to a Nigerian bank account. The API verifies your transaction PIN and the destination account before debiting your wallet and initiating the bank transfer.
- Minimum withdrawal: ₦1,000.
- Fee: Added on top of
amountand returned asdata.fee. - Account name: Resolved automatically through name enquiry. You do not send
accountName. - Settlement: The response confirms acceptance; the final outcome is delivered by webhook.
- Test mode: Test keys run the full validation flow without debiting your wallet or initiating a transfer.
Request Body
Prop
Type
The destination account name is resolved server-side by name enquiry and returned in data.bank.accountName. Do not send an accountName field.
Example
curl -X POST https://app.kdadatasub.name.ng/api/profile/withdraw \
-H "Authorization: Bearer kds_test_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"bankCode": "011",
"accountNumber": "0123456789",
"narration": "Withdrawal to First Bank",
"pin": "1234"
}'const response = await fetch("https://app.kdadatasub.name.ng/api/profile/withdraw", {
method: "POST",
headers: {
"Authorization": "Bearer kds_test_YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: 5000,
bankCode: "011",
accountNumber: "0123456789",
narration: "Withdrawal to First Bank",
pin: "1234"
})
});
const result = await response.json();
console.log(result);import requests
response = requests.post(
"https://app.kdadatasub.name.ng/api/profile/withdraw",
headers={
"Authorization": "Bearer kds_test_YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"amount": 5000,
"bankCode": "011",
"accountNumber": "0123456789",
"narration": "Withdrawal to First Bank",
"pin": "1234",
},
)
data = response.json()
print(data)<?php
$ch = curl_init("https://app.kdadatasub.name.ng/api/profile/withdraw");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer kds_test_YOUR_API_KEY",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"amount" => 5000,
"bankCode" => "011",
"accountNumber" => "0123456789",
"narration" => "Withdrawal to First Bank",
"pin" => "1234",
]),
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
print_r($data);Response
A successful request returns:
{
"success": true,
"message": "Withdrawal request received!",
"transactionId": "WITHDRAW1234567890REQ",
"data": {
"amount": 5000,
"fee": 50,
"total": 5050,
"status": "PENDING",
"bank": {
"bankCode": "011",
"bankName": "First Bank of Nigeria",
"accountNumber": "0123456789",
"accountName": "JOHN DOE"
},
"narration": "Withdrawal to First Bank",
"createdAt": "2026-06-01T12:00:00.000Z"
}
}Response Fields
Prop
Type
Settlement Webhooks
Withdrawals settle asynchronously. Accepted requests typically return status: "PENDING", then the final result is delivered to your subscribed webhook endpoint:
| Event | Meaning |
|---|---|
WITHDRAWAL_SUCCESS | The bank transfer completed. |
WITHDRAWAL_FAILED | The transfer failed or was reversed. amount + fee is refunded to your wallet. |
Each payload includes the transactionId, amount, fee, total, bank details, narration, and bank settlement references. A failed event also includes errorMessage.
See Webhooks for the payload contract and signature verification.
Test keys never emit WITHDRAWAL_SUCCESS or WITHDRAWAL_FAILED. Test withdrawals are simulated and never reach a bank. You can exercise your webhook handler with the Test button in Developer Settings after subscribing to the Withdrawal category.
Errors
| Status | Code | Cause |
|---|---|---|
400 | VALIDATION_ERROR | Invalid or missing body fields, unknown bankCode, failed account verification, amount below ₦1,000, or no transaction PIN set. |
401 | UNAUTHORIZED | Missing or invalid API key. |
402 | INSUFFICIENT_BALANCE | Wallet balance is less than amount + fee. |
403 | FORBIDDEN | Services-scoped key, incorrect PIN, or PIN temporarily locked. |
404 | NOT_FOUND | Wallet not found. |
405 | METHOD_NOT_ALLOWED | The endpoint was called with a method other than POST. |
412 | PRECONDITION_FAILED | Withdrawals are disabled for this API key. |
429 | RATE_LIMITED | The API key rate limit was exceeded. |
500 | INTERNAL_ERROR | The disbursement provider failed. The debit is refunded. |
Validation failures include a details array identifying the invalid fields. See Error Handling & Codes for the complete format.
Withdrawals Disabled
Every API key starts with withdrawals disabled, including live keys. Enable Allow Withdrawals for the key before retrying.
{
"success": false,
"error": {
"code": "PRECONDITION_FAILED",
"message": "Withdrawals are disabled for this API key. Enable the 'Allow Withdrawals' toggle in your developer settings to accept withdrawal requests."
}
}Invalid Destination Account
The destination account is verified before any wallet debit. If verification fails, no funds are moved and no transaction is created.
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid account number for the given bank code. Verify the details and try again."
}
}Incorrect PIN
An incorrect transaction PIN returns 403 FORBIDDEN. Five failed attempts trigger a temporary lockout, which lengthens if failures continue. A user without a transaction PIN configured receives 400 VALIDATION_ERROR.
Unsupported Bank
If bankCode does not match a supported bank, the request is rejected before account verification:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "No bank found for bankCode '999'."
}
}Best Practices & Next Steps
Check the balance first. The wallet must cover amount + fee, not just amount. Use the Check Balance endpoint before submitting large withdrawals.
Use webhooks for settlement. Do not poll or retry based on a PENDING response. Wait for WITHDRAWAL_SUCCESS or WITHDRAWAL_FAILED; retrying a withdrawal that was already accepted can create a second transfer.
Protect the PIN. Send the PIN only in the HTTPS request body. Never log it, store it, or place it in a query parameter. Treat the withdrawal transactionId as the reference for support and webhook reconciliation.
Related Resources
Check Balance
Confirm your available wallet balance before withdrawing.
Transaction History
Review past transactions and their statuses.
Supported Banks
Find the bank code for the destination account.
Webhooks
Receive signed withdrawal settlement events.
Error Handling & Codes
Review error codes, HTTP statuses, and recovery steps.