KDA Turbo
API Docs
Account

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 amount and returned as data.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:

EventMeaning
WITHDRAWAL_SUCCESSThe bank transfer completed.
WITHDRAWAL_FAILEDThe 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

StatusCodeCause
400VALIDATION_ERRORInvalid or missing body fields, unknown bankCode, failed account verification, amount below ₦1,000, or no transaction PIN set.
401UNAUTHORIZEDMissing or invalid API key.
402INSUFFICIENT_BALANCEWallet balance is less than amount + fee.
403FORBIDDENServices-scoped key, incorrect PIN, or PIN temporarily locked.
404NOT_FOUNDWallet not found.
405METHOD_NOT_ALLOWEDThe endpoint was called with a method other than POST.
412PRECONDITION_FAILEDWithdrawals are disabled for this API key.
429RATE_LIMITEDThe API key rate limit was exceeded.
500INTERNAL_ERRORThe 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.