> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Afrique du Sud - vérification du titulaire de compte

> Didit vérifie le titulaire d'un compte bancaire en Afrique du Sud via une base de données officielle. Dès $0.40 par appel, sans contrat ni minimum.

Confirme qu'un compte bancaire sud-africain appartient au titulaire désigné via le service interbancaire Account Holder Verification. Didit met ce service à disposition via `POST /v3/database-validation/` afin que vous puissiez vérifier les données soumises auprès de la source faisant autorité et recevoir des résultats de correspondance normalisés.

## Couverture

* **Couverture :** —
* **Pays :** Afrique du Sud
* **Identifiant du service :** `zaf_bank_account_holder`
* **Domaine de données :** Financier
* **Catégorie :** Banque

## Entrées

| Champ | Obligatoire | Exemple |
| - | -: | - |
| `national_id` | Oui | `8708150847085` |
| `bank_account_number` | Oui | `1234567890` |
| `bank_name` | Oui | `ABSA` |
| `account_type` | Non | `current` |
| `first_name` | Non | `John` |
| `last_name` | Non | `Doe` |
| `initials` | Non | `JD` |
| `email` | Non | `john.doe@example.com` |
| `phone_number` | Non | `+15550101000` |
| `vendor_data` | Non | `user-1234` |

* **Entrées obligatoires :** `national_id`, `bank_account_number`, `bank_name`
* **Entrées facultatives :** `account_type`, `first_name`, `last_name`, `initials`, `email`, `phone_number`, `vendor_data`
* **Consentement :** Obligatoire
* **Disponibilité dans le workflow :** Disponible dans un workflow avec mappage des champs
* **Couverture :** —
* **Prix :** \$0.40 par requête réussie

Dans un workflow, `bank_account_number` et `bank_name` ne figurent pas sur un document d'identité, donc aucune étape ne les produit seule. Collectez-les à partir d'une réponse à un Questionnaire, d'un champ extrait par Document AI ou d'une valeur que vous envoyez lors de la création de la session, puis mappez chacune d'elles sur l'étape de validation en base de données. Tant qu'elles ne sont pas mappées, l'éditeur de workflow affiche ce service comme désactivé et indique l'entrée manquante. À l'exécution, une entrée manquante signifie que la vérification est ignorée avant toute requête : vous n'êtes donc jamais facturé pour une base de données qui n'a pas pu être interrogée.

## Paramètres du corps de requête

**issuing\_state** `string` *required* (default: ZAF)

Code pays ISO 3166-1 alpha-3 de ce service de base de données.

Exemple : `ZAF`

**services** `string` *required* (default: zaf\_bank\_account\_holder)

Tableau contenant l'identifiant de ce service. Spécifier précisément le service limite la requête à cette base de données.

Exemple : `zaf_bank_account_holder`

**consent** `boolean` *required* (default: true)

Consentement explicite de l'utilisateur final pour ce service.

Exemple : `true`

**national\_id** `string` *required* (default: 8708150847085)

Numéro d'identité national pour ce service.

Exemple : `8708150847085`

**bank\_account\_number** `string` *required* (default: 1234567890)

Valeur de `bank_account_number` requise par ce service de base de données.

Exemple : `1234567890`

**bank\_name** `string` *required* (default: ABSA)

Valeur de `bank_name` requise par ce service de base de données.

Exemple : `ABSA`

**account\_type** `string` (default: current)

Valeur de `account_type` requise par ce service de base de données.

Exemple : `current`

**first\_name** `string`

Prénom à valider.

Exemple : `John`

**last\_name** `string` (default: Doe)

Nom de famille à valider.

Exemple : `Doe`

**initials** `string` (default: JD)

Valeur de `initials` requise par ce service de base de données.

Exemple : `JD`

**email** `string`

Adresse e-mail.

Exemple : `john.doe@example.com`

**phone\_number** `string`

Valeur de `phone_number` requise par ce service de base de données.

Exemple : `+15550101000`

**vendor\_data** `string` (default: user-1234)

Votre référence utilisateur stable pour cette personne, par exemple votre identifiant utilisateur interne. Didit s'en sert pour relier les vérifications autonomes au même utilisateur final et réduire le bruit des détections de doublons.

Exemple : `user-1234`

## Règles de saisie et notes de validation

* `account_type` doit être l'une des valeurs de type de compte AVS sud-africaines : `current`, `savings`, `transmission`, `subscriptionShare`, `notKnown`. La correspondance ne tient pas compte de la casse. Omettez le champ lorsque vous ne connaissez pas le type de compte : Didit envoie alors `notKnown`, et le contrôle croisé du type de compte est signalé comme non confirmé au lieu de faire échouer la requête.

## Comment l'appeler

```bash cURL theme={null}
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"
```

Chaque appel réussi renvoie un code HTTP `200`. Le champ **`outcome_code`** indique ce qui s'est réellement passé, en distinguant par exemple une véritable non-correspondance biométrique (`BIOMETRIC_NO_MATCH`) d'un selfie illisible (`BIOMETRIC_IMAGE_UNUSABLE`). Le `status` affiché est le statut par défaut de la fonctionnalité ; vos [actions Partial Match / No Match configurées](https://docs.didit.me/fr/core-technology/database-validation/database-validation-warnings.md) peuvent le remplacer.

**`MATCH`** : le registre a confirmé l'identité, et chaque champ vérifié correspond.

```json 200 OK — MATCH theme={null}
{
  "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`** : le numéro d'identification a été trouvé, mais un ou plusieurs champs personnels (généralement le nom ou la date de naissance) ne correspondent pas entièrement.

```json 200 OK — PARTIAL_MATCH theme={null}
{
  "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`** : le registre n'a renvoyé aucune correspondance pour les données soumises.

```json 200 OK — NO_MATCH theme={null}
{
  "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"
      }
    }
  ]
}
```

## Données renvoyées

Les champs exacts présents dans `source_data` dépendent de ce que renvoie le registre. L'exemple généré pour `zaf_bank_account_holder` documente actuellement la structure normalisée suivante :

* `accepts_credits`
* `accepts_debits`
* `account_found`
* `account_number_length_valid`
* `account_open`
* `account_type_match`
* `id_match`
* `identification_number`
* `initials_match`
* `surname_match`

## Tarifs et SLA

Les requêtes de vérification du titulaire de compte bancaire pour l'Afrique du Sud ne sont facturées que lorsque Didit reçoit un résultat concluant de la source de validation.

* **Prix par appel :** \$0.40 USD.
* **Facturation :** par requête réussie. Vous n'êtes pas facturé lorsque le registre est injoignable, lorsque des champs obligatoires sont manquants, ou lorsque la requête est rejetée avant d'atteindre la source.
* **Latence :** p95 typique \< 2 s.
* **Disponibilité :** 99.9 % par trimestre du côté de Didit ; la disponibilité de la source en aval varie selon le pays et le jeu de données.

## Ce que confirme un MATCH

La vérification du titulaire de compte (AVS) est un ensemble de contrôles croisés oui/non, et non une recherche d'enregistrement. La source ne renvoie jamais de nom, de numéro d'identité ni de solde de compte : elle prend les informations que vous envoyez, les compare à ce que la banque détient pour ce compte, et répond à chaque comparaison par un indicateur. `source_data` contient ces indicateurs :

* **`account_found`** - la banque détient un compte portant ce numéro. Tout le reste est sans signification lorsque cette valeur est `false`.
* **`account_open`** - le compte est ouvert. Un compte peut être trouvé et pourtant fermé.
* **`id_match`** - le compte est associé au numéro d'identité national que vous avez soumis. C'est le contrôle pour lequel le service existe.
* **`initials_match`**, **`surname_match`** - les `initials` et `last_name` soumis correspondent au titulaire enregistré.
* **`account_type_match`** - le `account_type` soumis correspond au type réel du compte.
* **`email_match`**, **`phone_match`** - l'`email` et le `phone_number` soumis correspondent aux coordonnées que la banque détient.
* **`accepts_debits`**, **`accepts_credits`** - le compte peut recevoir des ordres de prélèvement et des crédits.
* **`account_number_length_valid`** - le numéro de compte a la longueur attendue par la banque.

Le code de résultat en synthétise les indicateurs :

* **`MATCH`** - le compte existe et chaque champ soumis correspond, `id_match` compris.
* **`PARTIAL_MATCH`** - le compte appartient au numéro d'identité national soumis, mais au moins un autre champ envoyé ne correspond pas (un nom de famille orthographié différemment dans les registres de la banque, par exemple). `outcome_detail` indique les champs en échec.
* **`NO_MATCH`** - soit la banque ne détient aucun compte correspondant à ce numéro, soit le compte existe mais appartient à une autre personne. Ce sont deux réponses concluantes sur la personne.

Un contrôle croisé que vous n'avez pas demandé n'est ni compté ni signalé. AVS répond `N` à chaque champ non fourni, donc `surname_match`, `initials_match`, `email_match`, `phone_match` et `account_type_match` n'apparaissent dans `source_data` que si vous avez envoyé l'entrée correspondante. L'envoi de seulement `national_id`, `bank_account_number` et `bank_name` produit donc quand même un `MATCH` complet.

## Valeurs acceptées pour bank\_name et account\_type

`bank_name` et `account_type` sont des énumérations. Une valeur absente de l'une de ces listes est refusée avant l'exécution de la requête : une faute de frappe vous coûte donc un aller-retour, et non une facturation.

`account_type` est facultatif et accepte, sans tenir compte de la casse :

`current` · `savings` · `transmission` · `subscriptionShare` · `notKnown`

Omettez-le si vous ne connaissez pas le type de compte. Didit envoie alors `notKnown`, la requête aboutit, et `account_type_match` revient à `false` car il n'y avait rien à comparer. Toute autre valeur - `checking`, `cheque`, `bond` - est rejetée avec un `400` qui liste l'ensemble accepté.

`bank_name` est obligatoire, et ces valeurs sont confirmées acceptées (sans tenir compte de la casse) :

`ABSA` · `Capitec` · `FNB` · `Nedbank` · `Standard Bank` · `Investec` · `African Bank` · `Discovery Bank` · `Old Mutual` · `Bank Zero` · `Tyme` · `Grindrod`

Les comptes grand public de toutes ces banques sont couverts, Capitec compris. Envoyez la forme courte exactement telle qu'elle figure dans la liste : `Capitec`, et non `Capitec Bank` ; `Tyme`, et non `TymeBank`. Une banque non reconnue revient avec un résultat `REGISTRY_ERROR` dont `outcome_detail` indique le champ que la source a refusé.

## Distinguer un compte inexistant d'une source indisponible

Ces situations correspondent à des réponses HTTP différentes, aucune analyse de messages d'erreur n'est donc nécessaire :

* **Le compte n'existe pas** - `200 OK`, `match_type: "no_match"`, `outcome_code: "NO_MATCH"`, `source_data.account_found: false`. Une vraie réponse de la banque. Facturée, et votre action en cas d'absence de correspondance s'applique.
* **La source n'a pas répondu** - `502 Bad Gateway` avec `validation_errors[].code` = `empty_provider_response` et `retryable: true`. Rien n'a été établi concernant la personne, rien n'est facturé, et la requête peut être relancée sans risque.
* **La source a refusé d'exécuter la requête** - `400 Bad Request` avec `validation_errors[].code` = `provider_rejected_input` et `retryable: false`. La source n'a jamais effectué la recherche, car elle n'aurait pas accepté ce qui lui a été envoyé. Corrigez les données ; réessayer avec les mêmes valeurs ne peut pas réussir. Rien n'est facturé.
* **La source a répondu et rejeté votre saisie** - `502 Bad Gateway` avec `validation_errors[].code` = `provider_invalid_input` (par exemple un numéro de compte qui échoue au contrôle de format de la banque). Réessayer ne sert à rien tant que la saisie n'est pas corrigée ; rien n'est facturé.
* **Votre requête n'a jamais quitté Didit** - `400 Bad Request`, indiquant le champ à corriger. Rien n'est facturé.

Seule la première de ces réponses concerne la personne. Considérez un `502` comme transitoire - AVS transmet la requête à la banque du titulaire plutôt que de lire un registre, c'est donc le plus lent des services sud-africains - et réessayez avec un délai progressif.

## Pour aller plus loin

* [Vue d'ensemble de la validation en base de données pour l'Afrique du Sud](https://docs.didit.me/api-reference/database-validation/south-africa.md)
* [Vue d'ensemble de la validation en base de données](https://docs.didit.me/fr/core-technology/database-validation/overview.md)
* [Codes de résultat](https://docs.didit.me/fr/core-technology/database-validation/database-validation-outcome-codes.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.