Validate bank account
curl --request POST \
--url https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"accountNumber": "1234567890",
"bankCode": "KCB",
"countryCode": "KE",
"accountName": "Jane Doe"
}
'import requests
url = "https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank"
payload = {
"accountNumber": "1234567890",
"bankCode": "KCB",
"countryCode": "KE",
"accountName": "Jane Doe"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
accountNumber: '1234567890',
bankCode: 'KCB',
countryCode: 'KE',
accountName: 'Jane Doe'
})
};
fetch('https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'accountNumber' => '1234567890',
'bankCode' => 'KCB',
'countryCode' => 'KE',
'accountName' => 'Jane Doe'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank"
payload := strings.NewReader("{\n \"accountNumber\": \"1234567890\",\n \"bankCode\": \"KCB\",\n \"countryCode\": \"KE\",\n \"accountName\": \"Jane Doe\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"accountNumber\": \"1234567890\",\n \"bankCode\": \"KCB\",\n \"countryCode\": \"KE\",\n \"accountName\": \"Jane Doe\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"accountNumber\": \"1234567890\",\n \"bankCode\": \"KCB\",\n \"countryCode\": \"KE\",\n \"accountName\": \"Jane Doe\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "Bank account validated successfully.",
"data": {
"isValid": true,
"isSupported": true,
"accountType": "mobile_money",
"requiresInternationalization": false,
"countryCode": "KE",
"countryName": "Kenya",
"callingCode": "<string>",
"internationalNumber": "+254712345678",
"nationalNumber": "0712345678",
"bankName": "<string>",
"currency": "KES",
"network": "MTN",
"mno": "SAFARICOM KENYA",
"message": "<string>",
"accountName": "JANE MARY DOE",
"nameLookupStatus": "MATCHED",
"nameLookupReason": "Name lookup is not available for this payout corridor.",
"matchScore": 95,
"accountStatus": "ACTIVE",
"nameMatch": {
"provided": "Jane Doe",
"score": 100,
"matched": true,
"threshold": 70,
"masked": false
}
}
}{
"success": false,
"message": "Invalid request",
"data": {}
}{
"success": false,
"message": "Unauthorized",
"data": {}
}Customer Management
Validate Bank Account
Validates a bank account number format for a given country and bank code, and whether the country is supported.
POST
/
api
/
v3
/
customer
/
validate
/
bank
Validate bank account
curl --request POST \
--url https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"accountNumber": "1234567890",
"bankCode": "KCB",
"countryCode": "KE",
"accountName": "Jane Doe"
}
'import requests
url = "https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank"
payload = {
"accountNumber": "1234567890",
"bankCode": "KCB",
"countryCode": "KE",
"accountName": "Jane Doe"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
accountNumber: '1234567890',
bankCode: 'KCB',
countryCode: 'KE',
accountName: 'Jane Doe'
})
};
fetch('https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'accountNumber' => '1234567890',
'bankCode' => 'KCB',
'countryCode' => 'KE',
'accountName' => 'Jane Doe'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank"
payload := strings.NewReader("{\n \"accountNumber\": \"1234567890\",\n \"bankCode\": \"KCB\",\n \"countryCode\": \"KE\",\n \"accountName\": \"Jane Doe\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"accountNumber\": \"1234567890\",\n \"bankCode\": \"KCB\",\n \"countryCode\": \"KE\",\n \"accountName\": \"Jane Doe\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sandbox-api.kotanipay.com/api/v3/customer/validate/bank")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"accountNumber\": \"1234567890\",\n \"bankCode\": \"KCB\",\n \"countryCode\": \"KE\",\n \"accountName\": \"Jane Doe\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "Bank account validated successfully.",
"data": {
"isValid": true,
"isSupported": true,
"accountType": "mobile_money",
"requiresInternationalization": false,
"countryCode": "KE",
"countryName": "Kenya",
"callingCode": "<string>",
"internationalNumber": "+254712345678",
"nationalNumber": "0712345678",
"bankName": "<string>",
"currency": "KES",
"network": "MTN",
"mno": "SAFARICOM KENYA",
"message": "<string>",
"accountName": "JANE MARY DOE",
"nameLookupStatus": "MATCHED",
"nameLookupReason": "Name lookup is not available for this payout corridor.",
"matchScore": 95,
"accountStatus": "ACTIVE",
"nameMatch": {
"provided": "Jane Doe",
"score": 100,
"matched": true,
"threshold": 70,
"masked": false
}
}
}{
"success": false,
"message": "Invalid request",
"data": {}
}{
"success": false,
"message": "Unauthorized",
"data": {}
}Checks a bank account before you pay it: the account number format, the bank code,
and whether the country is supported.
With name lookup enabled, the response also carries the name registered on the
account and its status — a frozen or restricted account will usually reject an
incoming payment, so it’s worth checking before you send.
Reading the result
nameLookupStatus tells you whether the name resolved, and nameLookupReason says
why when it didn’t — so you can tell an account that doesn’t exist from a corridor
that can’t resolve names from something worth retrying.
isValid reports the account number format, and flips to false once a lookup
proves the destination unusable. A name mismatch is different: the account is
real, so isValid stays true and nameMatch.matched is what tells you the
holder isn’t who you expected.
Full status meanings, retry guidance, and how these fields relate are on
Account Validation.
Use the account test values to reproduce
frozen, unresolvable and mismatched-name accounts on demand.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
application/json