curl -X POST "https://verification.didit.me/v3/database-validation/" \
-H "x-api-key: YOUR_API_KEY" \
-F "issuing_state=ZAF" \
-F "services=zaf_bank_account_holder" \
-F "vendor_data=user-1234" \
-F "consent=true" \
-F "national_id=8708150847085" \
-F "bank_account_number=1234567890" \
-F "bank_name=ABSA" \
-F "account_type=current" \
-F "last_name=Doe" \
-F "initials=JD"
from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.database_validation.validate(
issuing_state="ZAF",
services=["zaf_bank_account_holder"],
consent=True,
last_name="Doe",
national_id="8708150847085",
initials="JD",
bank_name="ABSA",
bank_account_number="1234567890",
account_type="current",
vendor_data="user-1234",
)
print(result)
import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.databaseValidation.validate({
issuing_state: "ZAF",
services: ["zaf_bank_account_holder"],
consent: true,
last_name: "Doe",
national_id: "8708150847085",
initials: "JD",
bank_name: "ABSA",
bank_account_number: "1234567890",
account_type: "current",
vendor_data: "user-1234",
});
console.log(result);
ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.DatabaseValidation.Validate(ctx, didit.DatabaseValidationValidateParams{
IssuingState: "ZAF",
Services: didit.Some([]string{"zaf_bank_account_holder"}),
Consent: didit.Bool(true),
LastName: didit.String("Doe"),
NationalID: didit.String("8708150847085"),
Initials: didit.String("JD"),
BankName: didit.String("ABSA"),
BankAccountNumber: didit.String("1234567890"),
AccountType: didit.String("current"),
VendorData: didit.String("user-1234"),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)
import java.util.List;
import me.didit.Didit;
import me.didit.types.DatabaseValidationValidateParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.databaseValidation().validate(DatabaseValidationValidateParams.builder()
.issuingState("ZAF")
.services(List.of("zaf_bank_account_holder"))
.consent(true)
.lastName("Doe")
.nationalId("8708150847085")
.initials("JD")
.bankName("ABSA")
.bankAccountNumber("1234567890")
.accountType("current")
.vendorData("user-1234")
.build());
System.out.println(result);
<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->databaseValidation->validate([
'issuing_state' => 'ZAF',
'services' => ['zaf_bank_account_holder'],
'consent' => true,
'last_name' => 'Doe',
'national_id' => '8708150847085',
'initials' => 'JD',
'bank_name' => 'ABSA',
'bank_account_number' => '1234567890',
'account_type' => 'current',
'vendor_data' => 'user-1234',
]);
print_r($result->toArray());
require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.database_validation.validate(
issuing_state: "ZAF",
services: ["zaf_bank_account_holder"],
consent: true,
last_name: "Doe",
national_id: "8708150847085",
initials: "JD",
bank_name: "ABSA",
bank_account_number: "1234567890",
account_type: "current",
vendor_data: "user-1234"
)
puts result.to_h
using System;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.DatabaseValidation.ValidateAsync(new DatabaseValidationValidateParams
{
IssuingState = "ZAF",
Services = new string[] { "zaf_bank_account_holder" },
Consent = true,
LastName = "Doe",
NationalId = "8708150847085",
Initials = "JD",
BankName = "ABSA",
BankAccountNumber = "1234567890",
AccountType = "current",
VendorData = "user-1234",
});
Console.WriteLine(JsonSerializer.Serialize(result));
{
"request_id": "req_01H…",
"status": "Approved",
"issuing_state": "ZAF",
"match_type": "full_match",
"validations": [
{
"outcome_code": "MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": true,
"accepts_debits": true,
"account_found": true,
"account_number_length_valid": true,
"account_open": true,
"account_type_match": true,
"id_match": true,
"identification_number": "SAMPLE-ID-12345",
"initials_match": true,
"surname_match": true
},
"validation": {
"full_name": "full_match",
"identification_number": "full_match"
}
}
]
}
{
"request_id": "req_01H…",
"status": "In Review",
"issuing_state": "ZAF",
"match_type": "partial_match",
"validations": [
{
"outcome_code": "PARTIAL_MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": true,
"accepts_debits": true,
"account_found": true,
"account_number_length_valid": false,
"account_open": true,
"account_type_match": true,
"id_match": true,
"identification_number": "SAMPLE-ID-12345",
"initials_match": true,
"surname_match": false
},
"validation": {
"full_name": "partial_match",
"identification_number": "full_match"
}
}
]
}
{
"request_id": "req_01H…",
"status": "Declined",
"issuing_state": "ZAF",
"match_type": "no_match",
"validations": [
{
"outcome_code": "NO_MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": false,
"accepts_debits": false,
"account_found": false,
"account_number_length_valid": false,
"account_open": false,
"account_type_match": false,
"id_match": false,
"identification_number": "NO_MATCH",
"initials_match": false,
"surname_match": false
},
"validation": {
"identification_number": "no_match"
}
}
]
}
🇿🇦 South Africa
South Africa - Bank Account Holder Verification API
South Africa - Bank Account Holder Verification: authoritative South Africa database lookup via Didit. Pay-per-call from $0.40, no contracts, no minimums.
POST
/
v3
/
database-validation
/
curl -X POST "https://verification.didit.me/v3/database-validation/" \
-H "x-api-key: YOUR_API_KEY" \
-F "issuing_state=ZAF" \
-F "services=zaf_bank_account_holder" \
-F "vendor_data=user-1234" \
-F "consent=true" \
-F "national_id=8708150847085" \
-F "bank_account_number=1234567890" \
-F "bank_name=ABSA" \
-F "account_type=current" \
-F "last_name=Doe" \
-F "initials=JD"
from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.database_validation.validate(
issuing_state="ZAF",
services=["zaf_bank_account_holder"],
consent=True,
last_name="Doe",
national_id="8708150847085",
initials="JD",
bank_name="ABSA",
bank_account_number="1234567890",
account_type="current",
vendor_data="user-1234",
)
print(result)
import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.databaseValidation.validate({
issuing_state: "ZAF",
services: ["zaf_bank_account_holder"],
consent: true,
last_name: "Doe",
national_id: "8708150847085",
initials: "JD",
bank_name: "ABSA",
bank_account_number: "1234567890",
account_type: "current",
vendor_data: "user-1234",
});
console.log(result);
ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.DatabaseValidation.Validate(ctx, didit.DatabaseValidationValidateParams{
IssuingState: "ZAF",
Services: didit.Some([]string{"zaf_bank_account_holder"}),
Consent: didit.Bool(true),
LastName: didit.String("Doe"),
NationalID: didit.String("8708150847085"),
Initials: didit.String("JD"),
BankName: didit.String("ABSA"),
BankAccountNumber: didit.String("1234567890"),
AccountType: didit.String("current"),
VendorData: didit.String("user-1234"),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)
import java.util.List;
import me.didit.Didit;
import me.didit.types.DatabaseValidationValidateParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.databaseValidation().validate(DatabaseValidationValidateParams.builder()
.issuingState("ZAF")
.services(List.of("zaf_bank_account_holder"))
.consent(true)
.lastName("Doe")
.nationalId("8708150847085")
.initials("JD")
.bankName("ABSA")
.bankAccountNumber("1234567890")
.accountType("current")
.vendorData("user-1234")
.build());
System.out.println(result);
<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->databaseValidation->validate([
'issuing_state' => 'ZAF',
'services' => ['zaf_bank_account_holder'],
'consent' => true,
'last_name' => 'Doe',
'national_id' => '8708150847085',
'initials' => 'JD',
'bank_name' => 'ABSA',
'bank_account_number' => '1234567890',
'account_type' => 'current',
'vendor_data' => 'user-1234',
]);
print_r($result->toArray());
require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.database_validation.validate(
issuing_state: "ZAF",
services: ["zaf_bank_account_holder"],
consent: true,
last_name: "Doe",
national_id: "8708150847085",
initials: "JD",
bank_name: "ABSA",
bank_account_number: "1234567890",
account_type: "current",
vendor_data: "user-1234"
)
puts result.to_h
using System;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.DatabaseValidation.ValidateAsync(new DatabaseValidationValidateParams
{
IssuingState = "ZAF",
Services = new string[] { "zaf_bank_account_holder" },
Consent = true,
LastName = "Doe",
NationalId = "8708150847085",
Initials = "JD",
BankName = "ABSA",
BankAccountNumber = "1234567890",
AccountType = "current",
VendorData = "user-1234",
});
Console.WriteLine(JsonSerializer.Serialize(result));
{
"request_id": "req_01H…",
"status": "Approved",
"issuing_state": "ZAF",
"match_type": "full_match",
"validations": [
{
"outcome_code": "MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": true,
"accepts_debits": true,
"account_found": true,
"account_number_length_valid": true,
"account_open": true,
"account_type_match": true,
"id_match": true,
"identification_number": "SAMPLE-ID-12345",
"initials_match": true,
"surname_match": true
},
"validation": {
"full_name": "full_match",
"identification_number": "full_match"
}
}
]
}
{
"request_id": "req_01H…",
"status": "In Review",
"issuing_state": "ZAF",
"match_type": "partial_match",
"validations": [
{
"outcome_code": "PARTIAL_MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": true,
"accepts_debits": true,
"account_found": true,
"account_number_length_valid": false,
"account_open": true,
"account_type_match": true,
"id_match": true,
"identification_number": "SAMPLE-ID-12345",
"initials_match": true,
"surname_match": false
},
"validation": {
"full_name": "partial_match",
"identification_number": "full_match"
}
}
]
}
{
"request_id": "req_01H…",
"status": "Declined",
"issuing_state": "ZAF",
"match_type": "no_match",
"validations": [
{
"outcome_code": "NO_MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": false,
"accepts_debits": false,
"account_found": false,
"account_number_length_valid": false,
"account_open": false,
"account_type_match": false,
"id_match": false,
"identification_number": "NO_MATCH",
"initials_match": false,
"surname_match": false
},
"validation": {
"identification_number": "no_match"
}
}
]
}
Confirms a South African bank account belongs to the named holder via the inter-bank Account Holder Verification service. Didit exposes this service through
POST /v3/database-validation/ so you can verify the submitted data against the authoritative source and receive normalized match results. Input format: national_id takes the South African ID number (13 digits, the last one a Luhn check digit).
Coverage
- Coverage: —
- Country: South Africa
- Service ID:
zaf_bank_account_holder - Data domain: Financial
- Category: Banking
Inputs
| Field | Required | Example |
|---|---|---|
national_id | Yes | 8708150847085 |
bank_account_number | Yes | 1234567890 |
bank_name | Yes | ABSA |
account_type | No | current |
first_name | No | John |
last_name | No | Doe |
initials | No | JD |
email | No | john.doe@example.com |
phone_number | No | +15550101000 |
vendor_data | No | user-1234 |
- Required inputs:
national_id,bank_account_number,bank_name - Optional inputs:
account_type,first_name,last_name,initials,email,phone_number,vendor_data - Consent: Required
- Workflow availability: Available in workflow with field mapping
- Coverage: —
- Price: $0.40 per successful query
bank_account_number and bank_name are not printed on an identity document, so no step produces them on their own. Collect them from a Questionnaire answer, a Document AI extraction field, or a value you send when you create the session, and map each one on the Database Validation step. Until they are mapped the workflow editor shows this service disabled and names the missing input; at run time a missing input means the check is skipped before any query, so you are never billed for a database that could not be asked.
Body parameters
Array containing this service ID. Pinning the service keeps the request scoped to this exact database.Example:
zaf_bank_account_holderNational identity number for this service.Example:
8708150847085bank_account_number value required by this database service.Example: 1234567890Your stable user reference for this person, such as your internal user ID. Didit uses it to link standalone checks to the same end user and reduce duplicate-detection noise.Example:
user-1234Input rules & validation notes
account_typemust be one of the South African AVS account types:current,savings,transmission,subscriptionShare,notKnown. Matching is case-insensitive. Omit the field when you do not know the account type - Didit then sendsnotKnownand the account-type cross-check is reported as not confirmed instead of failing the request.- South African ID number (13 digits, the last one a Luhn check digit)
national_idmust contain digits only; remove spaces, hyphens, and punctuation before sending the request.national_idmust be exactly 13 characters long.
How to call it
curl -X POST "https://verification.didit.me/v3/database-validation/" \
-H "x-api-key: YOUR_API_KEY" \
-F "issuing_state=ZAF" \
-F "services=zaf_bank_account_holder" \
-F "vendor_data=user-1234" \
-F "consent=true" \
-F "national_id=8708150847085" \
-F "bank_account_number=1234567890" \
-F "bank_name=ABSA" \
-F "account_type=current" \
-F "last_name=Doe" \
-F "initials=JD"
from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.database_validation.validate(
issuing_state="ZAF",
services=["zaf_bank_account_holder"],
consent=True,
last_name="Doe",
national_id="8708150847085",
initials="JD",
bank_name="ABSA",
bank_account_number="1234567890",
account_type="current",
vendor_data="user-1234",
)
print(result)
import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.databaseValidation.validate({
issuing_state: "ZAF",
services: ["zaf_bank_account_holder"],
consent: true,
last_name: "Doe",
national_id: "8708150847085",
initials: "JD",
bank_name: "ABSA",
bank_account_number: "1234567890",
account_type: "current",
vendor_data: "user-1234",
});
console.log(result);
ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.DatabaseValidation.Validate(ctx, didit.DatabaseValidationValidateParams{
IssuingState: "ZAF",
Services: didit.Some([]string{"zaf_bank_account_holder"}),
Consent: didit.Bool(true),
LastName: didit.String("Doe"),
NationalID: didit.String("8708150847085"),
Initials: didit.String("JD"),
BankName: didit.String("ABSA"),
BankAccountNumber: didit.String("1234567890"),
AccountType: didit.String("current"),
VendorData: didit.String("user-1234"),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)
import java.util.List;
import me.didit.Didit;
import me.didit.types.DatabaseValidationValidateParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.databaseValidation().validate(DatabaseValidationValidateParams.builder()
.issuingState("ZAF")
.services(List.of("zaf_bank_account_holder"))
.consent(true)
.lastName("Doe")
.nationalId("8708150847085")
.initials("JD")
.bankName("ABSA")
.bankAccountNumber("1234567890")
.accountType("current")
.vendorData("user-1234")
.build());
System.out.println(result);
<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->databaseValidation->validate([
'issuing_state' => 'ZAF',
'services' => ['zaf_bank_account_holder'],
'consent' => true,
'last_name' => 'Doe',
'national_id' => '8708150847085',
'initials' => 'JD',
'bank_name' => 'ABSA',
'bank_account_number' => '1234567890',
'account_type' => 'current',
'vendor_data' => 'user-1234',
]);
print_r($result->toArray());
require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.database_validation.validate(
issuing_state: "ZAF",
services: ["zaf_bank_account_holder"],
consent: true,
last_name: "Doe",
national_id: "8708150847085",
initials: "JD",
bank_name: "ABSA",
bank_account_number: "1234567890",
account_type: "current",
vendor_data: "user-1234"
)
puts result.to_h
using System;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.DatabaseValidation.ValidateAsync(new DatabaseValidationValidateParams
{
IssuingState = "ZAF",
Services = new string[] { "zaf_bank_account_holder" },
Consent = true,
LastName = "Doe",
NationalId = "8708150847085",
Initials = "JD",
BankName = "ABSA",
BankAccountNumber = "1234567890",
AccountType = "current",
VendorData = "user-1234",
});
Console.WriteLine(JsonSerializer.Serialize(result));
Every successful call returns HTTP 200. The outcome_code field tells you what actually happened — distinguishing, for example, a real biometric mismatch (BIOMETRIC_NO_MATCH) from a selfie that could not be read (BIOMETRIC_IMAGE_UNUSABLE). The status shown is the default feature status; your configured Partial Match / No Match actions can override it.MATCH — The registry confirmed the identity and every checked field matched.{
"request_id": "req_01H…",
"status": "Approved",
"issuing_state": "ZAF",
"match_type": "full_match",
"validations": [
{
"outcome_code": "MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": true,
"accepts_debits": true,
"account_found": true,
"account_number_length_valid": true,
"account_open": true,
"account_type_match": true,
"id_match": true,
"identification_number": "SAMPLE-ID-12345",
"initials_match": true,
"surname_match": true
},
"validation": {
"full_name": "full_match",
"identification_number": "full_match"
}
}
]
}
PARTIAL_MATCH — The identification number was found but one or more personal fields (usually name or date of birth) did not fully match.{
"request_id": "req_01H…",
"status": "In Review",
"issuing_state": "ZAF",
"match_type": "partial_match",
"validations": [
{
"outcome_code": "PARTIAL_MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": true,
"accepts_debits": true,
"account_found": true,
"account_number_length_valid": false,
"account_open": true,
"account_type_match": true,
"id_match": true,
"identification_number": "SAMPLE-ID-12345",
"initials_match": true,
"surname_match": false
},
"validation": {
"full_name": "partial_match",
"identification_number": "full_match"
}
}
]
}
NO_MATCH — The registry returned no match for the submitted data.{
"request_id": "req_01H…",
"status": "Declined",
"issuing_state": "ZAF",
"match_type": "no_match",
"validations": [
{
"outcome_code": "NO_MATCH",
"service_id": "zaf_bank_account_holder",
"service_name": "South Africa - Bank Account Holder Verification",
"source_data": {
"accepts_credits": false,
"accepts_debits": false,
"account_found": false,
"account_number_length_valid": false,
"account_open": false,
"account_type_match": false,
"id_match": false,
"identification_number": "NO_MATCH",
"initials_match": false,
"surname_match": false
},
"validation": {
"identification_number": "no_match"
}
}
]
}
Returned data
The exact fields surfaced insource_data depend on what the registry returns. The generated example for zaf_bank_account_holder currently documents this normalized shape:
accepts_creditsaccepts_debitsaccount_foundaccount_number_length_validaccount_openaccount_type_matchid_matchidentification_numberinitials_matchsurname_match
Pricing & SLAs
South Africa - Bank Account Holder Verification queries are billed only when Didit receives a conclusive result from the validation source.- Per-call price: $0.40 USD.
- Billing: per successful query. You are not charged when the registry is unreachable, when required fields are missing, or when the request is rejected before reaching the source.
- Latency: typical p95 < 2 s.
- Availability: 99.9% per quarter on Didit’s side; downstream source availability varies by country and dataset.
What a MATCH confirms
Account Holder Verification (AVS) is a set of yes/no cross-checks, not a record lookup. The source never returns a name, an ID number or an account balance - it takes the details you send, compares them against what the bank holds for that account, and answers each comparison with a flag.source_data carries those flags:
account_found- the bank holds an account with this number. Everything else is meaningless when this isfalse.account_open- the account is open. An account can be found but closed.id_match- the account is held by the national id you submitted. This is the check the service exists for.initials_match,surname_match- the submittedinitialsandlast_namematch the account holder on file.account_type_match- the submittedaccount_typematches the account’s real type.email_match,phone_match- the submittedemailandphone_numbermatch the contact details the bank holds.accepts_debits,accepts_credits- the account can receive debit orders and credits.account_number_length_valid- the account number is the length the bank expects.
MATCH- the account exists and every field you submitted matched,id_matchincluded.PARTIAL_MATCH- the account is held by the submitted national id, but at least one other field you sent did not match (a surname spelled differently on the bank’s records, for example).outcome_detailnames the fields that failed.NO_MATCH- either the bank holds no such account, or the account exists but belongs to someone else. Both are conclusive answers about the person.
N for every field that was not supplied, so surname_match, initials_match, email_match, phone_match and account_type_match appear in source_data only when you sent the matching input. Sending only national_id, bank_account_number and bank_name therefore still produces a full MATCH.
Accepted bank_name and account_type values
bank_name and account_type are enumerations. A value outside either list is refused before the query runs, so a typo costs you a round-trip rather than a charge.
account_type is optional and accepts, case-insensitively:
current · savings · transmission · subscriptionShare · notKnown
Omit it when you do not know the account type. Didit then sends notKnown, the request succeeds, and account_type_match comes back false because there was nothing to compare. Any other value - checking, cheque, bond - is rejected with a 400 that lists the accepted set.
bank_name is required, and these values are confirmed accepted (case-insensitive):
ABSA · Capitec · FNB · Nedbank · Standard Bank · Investec · African Bank · Discovery Bank · Old Mutual · Bank Zero · Tyme · Grindrod
Consumer accounts at all of these are in scope, Capitec included. Send the short form exactly as listed - Capitec, not Capitec Bank; Tyme, not TymeBank. An unrecognised bank comes back as an outcome of REGISTRY_ERROR whose outcome_detail names the field the source refused.
Telling a missing account apart from an unavailable source
These are different HTTP outcomes, so no parsing of error strings is needed:- The account does not exist -
200 OK,match_type: "no_match",outcome_code: "NO_MATCH",source_data.account_found: false. A real answer from the bank. Billed, and your no-match action applies. - The source did not answer -
502 Bad Gatewaywithvalidation_errors[].code=empty_provider_responseandretryable: true. Nothing was established about the person, nothing is billed, and the request is safe to retry. - The source refused to run the query -
400 Bad Requestwithvalidation_errors[].code=provider_rejected_inputandretryable: false. The source never searched, because it would not accept what it was sent. Correct the data; retrying the same values cannot succeed. Nothing is billed. - The source answered and rejected your input -
502 Bad Gatewaywithvalidation_errors[].code=provider_invalid_input(for example an account number that fails the bank’s own format check). Retrying is pointless until the input is corrected; nothing is billed. - Your request never left Didit -
400 Bad Request, listing the field to fix. Nothing is billed.
502 as transient - AVS relays to the account holder’s bank rather than reading a registry, so it is the slowest of the South African services - and retry with backoff.
Continue reading
Last modified on November 11, 2026