> For the complete documentation index, see [llms.txt](https://developer.fraudcheck.co.za/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.fraudcheck.co.za/identity/enhanced-identity-verification.md).

# Identity Verification - Enhanced

The enhanced form of identity verification confirms a consumer's identity against official records and returns the official ID photograph along with key biographic details. Two variants are available: an on file variant for faster, cheaper lookups against credit bureau records, and an online variant that queries live records in real time.

## When to use this

Use this service to:

* Retrieve an official ID photograph for KYC documentation
* Verify a consumer's name and ID number during account opening
* Check deceased status to flag potential identity fraud
* Capture an official photograph for onboarding or face-match workflows

## How it works

Submit an ID number, first name, and surname in a single POST request. The service matches the details against identity records and returns a synchronous response containing the verification outcome, biographic data, and a base64-encoded ID photograph (if available). Choose the `onfile` endpoint for faster lookups against recent snapshot data, or the `online` endpoint for real-time lookups against live records. Results can also be retrieved later by transaction ID.

| Property              | Value                                                                              |
| --------------------- | ---------------------------------------------------------------------------------- |
| Response type         | `Synchronous`                                                                      |
| Typical response time | 2–4s (onfile faster than online)                                                   |
| Result retrieval      | Returned inline; also retrievable via `GET /enhanced-idv/results/{transaction_id}` |
| Consent required      | `Yes`                                                                              |
| Region                | South Africa                                                                       |

## Authentication

```http
Authorization: Bearer {your_access_token}
```

## Endpoints

```http
POST /enhanced-idv/verify/onfile
POST /enhanced-idv/verify/online
GET  /enhanced-idv/results/{transaction_id}
```

| Endpoint         | Data freshness                   |
| ---------------- | -------------------------------- |
| `/verify/onfile` | Recent snapshot records (faster) |
| `/verify/online` | Live records (real time)         |

## Request

### Headers

| Header          | Required | Value              |
| --------------- | -------- | ------------------ |
| `Authorization` | Yes      | `Bearer {token}`   |
| `Content-Type`  | Yes      | `application/json` |

### Body parameters

| Field                          | Type    | Required | Description                                         |
| ------------------------------ | ------- | -------- | --------------------------------------------------- |
| `IdentityNo`                   | string  | Yes      | 13-digit South African ID number                    |
| `FirstName`                    | string  | Yes      | Consumer's first name. Must match official records. |
| `LastName`                     | string  | Yes      | Consumer's surname. Must match official records.    |
| `ConsentObtainedByDataSubject` | boolean | No       | Confirms POPIA consent. Recommended `true`.         |

### Example request

```json
{
  "IdentityNo": "8601015800086",
  "FirstName": "John",
  "LastName": "Smith",
  "ConsentObtainedByDataSubject": true
}
```

## Response

### Response fields

| Field                     | Type    | Description                                       |
| ------------------------- | ------- | ------------------------------------------------- |
| `success`                 | boolean | `true` if the request completed successfully      |
| `transaction_id`          | string  | Unique identifier for this verification           |
| `verification_passed`     | boolean | `true` if submitted details matched records       |
| `consumer_name`           | string  | Full name from official records                   |
| `id_number`               | string  | ID number from records                            |
| `deceased`                | boolean | `true` if the ID holder is marked as deceased     |
| `id_photo_available`      | boolean | Whether an official photograph was returned       |
| `message`                 | string  | Human-readable status message                     |
| `data.ha_names`           | string  | First name(s) from records                        |
| `data.ha_surname`         | string  | Surname from records                              |
| `data.ha_date_of_birth`   | string  | Date of birth (`YYYY-MM-DD`)                      |
| `data.ha_gender`          | string  | `M` or `F`                                        |
| `data.ha_nationality`     | string  | Nationality code                                  |
| `data.ha_marital_status`  | string  | Marital status                                    |
| `data.id_no_match_status` | string  | `Match` or `No Match`                             |
| `data.id_photo`           | string  | Base64-encoded JPEG of the official ID photograph |
| `data.id_photo_hash`      | string  | Integrity hash of the photograph                  |
| `data.id_photo_quality`   | string  | Quality assessment of the photograph              |

### Example response - verified

```json
{
  "success": true,
  "message": "ID verification completed successfully",
  "transaction_id": "d41d8cd98f00b204e9800998ecf8427e",
  "verification_passed": true,
  "consumer_name": "JOHN SMITH",
  "id_number": "8601015800086",
  "deceased": false,
  "id_photo_available": true,
  "data": {
    "ha_names": "JOHN",
    "ha_surname": "SMITH",
    "id_no_match_status": "Match",
    "ha_date_of_birth": "1986-01-15",
    "ha_gender": "M",
    "ha_nationality": "SA",
    "ha_marital_status": "Single",
    "id_photo": "base64_encoded_photo_data...",
    "id_photo_hash": "abc123def456...",
    "id_photo_quality": "Good"
  }
}
```

### Example response - ID not found

```json
{
  "success": false,
  "message": "ID not found in records",
  "transaction_id": "d41d8cd98f00b204e9800998ecf8427e",
  "verification_passed": false,
  "id_number": "8601015800086",
  "deceased": false,
  "id_photo_available": false,
  "data": { "id_no_match_status": "No Match" }
}
```

## Retrieving results later

Re-fetch a previously completed verification by transaction ID:

```http
GET /enhanced-idv/results/{transaction_id}
Authorization: Bearer {your_access_token}
```

## Errors

| HTTP | Code            | Meaning                              | Action                               |
| ---- | --------------- | ------------------------------------ | ------------------------------------ |
| 401  | `unauthorized`  | Token missing or expired             | Refresh your access token            |
| 403  | `forbidden`     | Account lacks access to this service | Contact your account manager         |
| 422  | `invalid_input` | Required field missing or invalid    | See `details.field`                  |
| 500  | `server_error`  | Unexpected error                     | Retry; contact support if persistent |

> An ID that cannot be found in records returns HTTP `200` with `success: false` and `verification_passed: false` - it is a successful API call with a negative result, not an error.

## Code examples

{% tabs %}
{% tab title="cURL" %}

```bash
BASE_URL="https://consumer-service-api.fraudcheckonline.co.za/consumer-service"

curl -X POST "$BASE_URL/enhanced-idv/verify/onfile" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "IdentityNo": "8601015800086",
    "FirstName": "John",
    "LastName": "Smith",
    "ConsentObtainedByDataSubject": true
  }'
```

{% endtab %}

{% tab title="Python" %}

```python
import base64, requests

BASE_URL = "https://consumer-service-api.fraudcheckonline.co.za/consumer-service"

response = requests.post(
    f"{BASE_URL}/enhanced-idv/verify/onfile",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={
        "IdentityNo": "8601015800086",
        "FirstName": "John",
        "LastName": "Smith",
        "ConsentObtainedByDataSubject": True,
    },
)
data = response.json()

if data["success"] and data["verification_passed"]:
    print(f"Verified: {data['consumer_name']}")
    if data["data"].get("id_photo"):
        with open("id_photo.jpg", "wb") as f:
            f.write(base64.b64decode(data["data"]["id_photo"]))
```

{% endtab %}

{% tab title="JavaScript (Node.js)" %}

```javascript
const BASE_URL = "https://consumer-service-api.fraudcheckonline.co.za/consumer-service";

const response = await fetch(`${BASE_URL}/enhanced-idv/verify/onfile`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    IdentityNo: "8601015800086",
    FirstName: "John",
    LastName: "Smith",
    ConsentObtainedByDataSubject: true,
  }),
});
const data = await response.json();

if (data.success && data.data.id_photo) {
  const photoUrl = `data:image/jpeg;base64,${data.data.id_photo}`;
}
```

{% endtab %}
{% endtabs %}

## Compliance & consent

* Obtain explicit consent from the data subject before submitting a request.
* Set `ConsentObtainedByDataSubject` to `true` to confirm consent has been captured.
* Retain proof of consent and any retrieved photographs in line with POPIA requirements.

## FAQ

<details>

<summary>What's the difference between <code>onfile</code> and <code>online</code>?</summary>

`onfile` uses recent snapshot data and is faster. `online` queries live records in real time and reflects the latest information.

</details>

<details>

<summary>What format is the photograph?</summary>

A base64-encoded JPEG returned in `data.id_photo`. Decode it to obtain the binary image.

</details>

<details>

<summary>What does <code>deceased: true</code> mean?</summary>

The ID holder is flagged as deceased in records. Treat this as a strong fraud signal and route to manual review or rejection.

</details>

<details>

<summary>Why might <code>verification_passed</code> be <code>false</code> even when the ID exists?</summary>

Submitted name or surname did not match records. Initials, nicknames, or spelling variants are common causes.

</details>

<details>

<summary>What client timeout should I configure?</summary>

Up to 30 seconds.

</details>

## Changelog

| Date       | Version | Change           |
| ---------- | ------- | ---------------- |
| 2026-04-11 | v1.0    | Initial release. |
