> 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/identity-document-authentication.md).

# Identity Document Authentication

Identity Document Authentication verifies the authenticity of an identity document - passport, driver's license, or national ID - and extracts the personal information embedded in it. Submit a scanned or photographed image of the document and receive an accept/reject/refer outcome along with the parsed details.

## When to use this

Use this service to:

* Verify the authenticity of a passport, driver's license, or national ID
* Extract name, ID number, date of birth, and other details from a document image
* Detect fraudulent or tampered identity documents during onboarding
* Confirm a document is in date and that the holder is over 18
* Capture document data for KYC and AML compliance records

## How it works

Submit a base64-encoded image of the document (and the back image, if applicable) along with the consumer's name and ID number. The service analyses document security features, extracts the data, and returns a synchronous response containing the parsed fields and an overall verification outcome. Results can also be retrieved later by transaction ID.

| Property              | Value                                                                          |
| --------------------- | ------------------------------------------------------------------------------ |
| Response type         | `Synchronous`                                                                  |
| Typical response time | 5–30s                                                                          |
| Result retrieval      | Returned inline; also retrievable via `GET /doc-auth/results/{transaction_id}` |
| Consent required      | `Yes`                                                                          |
| Region                | South Africa                                                                   |

***

## Authentication

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

***

## Endpoints

```http
POST /doc-auth/verify/passport
POST /doc-auth/verify/drivers-license
POST /doc-auth/verify/national-id
GET  /doc-auth/results/{transaction_id}
```

Use the endpoint that matches the document type being submitted.

***

## Request

### Headers

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

### Body parameters

| Field                          | Type    | Required | Description                                          |
| ------------------------------ | ------- | -------- | ---------------------------------------------------- |
| `IdentityNo`                   | string  | Yes      | Identity or passport number on the document          |
| `FirstName`                    | string  | Yes      | Consumer's first name                                |
| `LastName`                     | string  | Yes      | Consumer's surname                                   |
| `Document1`                    | string  | Yes      | Base64-encoded image of the front of the document    |
| `Document1Name`                | string  | Yes      | Filename for `Document1` (e.g. `passport_front.jpg`) |
| `Document2`                    | string  | No       | Base64-encoded image of the back of the document     |
| `Document2Name`                | string  | No       | Filename for `Document2`                             |
| `ConsentObtainedByDataSubject` | boolean | Yes      | Must be `true`. Confirms POPIA consent.              |
| `YourReference`                | string  | No       | Your internal reference, echoed in audit logs        |

### Document image requirements

* **Formats:** JPEG, PNG, or PDF
* **Resolution:** 300 DPI minimum
* **Size:** up to 5 MB per document
* **Quality:** clear, in-focus, full-colour, with the entire document visible
* **Sides:** submit both sides for dual-sided documents (driver's license, national ID card)

### Example request

```json
{
  "IdentityNo": "8601015800086",
  "FirstName": "John",
  "LastName": "Smith",
  "Document1": "base64_encoded_front_image...",
  "Document1Name": "passport_front.jpg",
  "Document2": "base64_encoded_back_image...",
  "Document2Name": "passport_back.jpg",
  "ConsentObtainedByDataSubject": true
}
```

***

## Response

### Response fields

| Field                      | Type   | Description                                          |
| -------------------------- | ------ | ---------------------------------------------------- |
| `transaction_id`           | string | Unique identifier for this verification              |
| `verification_result`      | string | `ACCEPTED`, `REJECTED`, or `REFER`                   |
| `verification_result_code` | string | Numeric code (`100`, `200`, `300`) - see table below |
| `id_type`                  | string | `PASSPORT`, `DRIVERS_LICENSE`, or `NATIONAL_ID`      |
| `first_name`               | string | First name extracted from the document               |
| `last_name`                | string | Surname extracted from the document                  |
| `id_number`                | string | ID/passport number extracted from the document       |
| `date_of_birth`            | string | Date of birth (`YYYY-MM-DD`)                         |
| `gender`                   | string | `M` or `F`                                           |
| `nationality`              | string | Nationality code (e.g. `ZA`)                         |
| `id_issue_country`         | string | Country that issued the document                     |
| `id_issue_date`            | string | Document issue date (`YYYY-MM-DD`)                   |
| `id_expiration_date`       | string | Document expiry date (`YYYY-MM-DD`)                  |
| `valid_id_number`          | string | `Y` or `N`                                           |
| `age_over_18`              | string | `Y` or `N`                                           |
| `address`                  | string | Address extracted from the document, if present      |

### Verification result codes

| Code  | Result     | Meaning                      |
| ----- | ---------- | ---------------------------- |
| `100` | `ACCEPTED` | Document verified and valid  |
| `200` | `REJECTED` | Document failed verification |
| `300` | `REFER`    | Manual review required       |

### Example response - accepted

```json
{
  "transaction_id": "d41d8cd98f00b204e9800998ecf8427e",
  "verification_result": "ACCEPTED",
  "verification_result_code": "100",
  "id_type": "PASSPORT",
  "first_name": "JOHN",
  "last_name": "SMITH",
  "id_number": "8601015800086",
  "date_of_birth": "1986-01-15",
  "gender": "M",
  "nationality": "ZA",
  "id_issue_country": "ZA",
  "id_issue_date": "2019-06-15",
  "id_expiration_date": "2029-06-14",
  "valid_id_number": "Y",
  "age_over_18": "Y",
  "address": "123 Main Street, Johannesburg, 2146"
}
```

### Example response - rejected

```json
{
  "transaction_id": "d41d8cd98f00b204e9800998ecf8427e",
  "verification_result": "REJECTED",
  "verification_result_code": "200",
  "first_name": "JOHN",
  "last_name": "SMITH",
  "id_number": "",
  "valid_id_number": "N"
}
```

***

## Retrieving results later

Re-fetch a previously completed verification by transaction ID:

```http
GET /doc-auth/results/{transaction_id}
Authorization: Bearer {your_access_token}
```

The response uses the same shape as the verification endpoint.

***

## 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         |
| 404  | `not_found`     | No result found for the supplied `transaction_id` | Check the transaction ID             |
| 422  | `invalid_input` | Required field missing or invalid                 | See `details.field`                  |
| 500  | `server_error`  | Unexpected error                                  | Retry; contact support if persistent |

{% hint style="info" %}
A document that fails verification returns HTTP `200` with `verification_result: "REJECTED"` - it is a successful API call with a negative outcome, not an error.
{% endhint %}

***

## Code examples

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

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

curl -X POST "$BASE_URL/doc-auth/verify/passport" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "IdentityNo": "8601015800086",
    "FirstName": "John",
    "LastName": "Smith",
    "Document1": "'"$(base64 -w0 passport_front.jpg)"'",
    "Document1Name": "passport_front.jpg",
    "ConsentObtainedByDataSubject": true
  }'
```

{% endtab %}

{% tab title="Python" %}

```python
import base64, requests

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

with open("passport_front.jpg", "rb") as f:
    front = base64.b64encode(f.read()).decode()

response = requests.post(
    f"{BASE_URL}/doc-auth/verify/passport",
    headers={
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    },
    json={
        "IdentityNo": "8601015800086",
        "FirstName": "John",
        "LastName": "Smith",
        "Document1": front,
        "Document1Name": "passport_front.jpg",
        "ConsentObtainedByDataSubject": True,
    },
)
data = response.json()

print(f"Result: {data['verification_result']}")
print(f"Name: {data['first_name']} {data['last_name']}")
print(f"Expires: {data.get('id_expiration_date')}")
```

{% endtab %}

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

```javascript
import fs from "node:fs/promises";

const front = (await fs.readFile("passport_front.jpg")).toString("base64");

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

const response = await fetch(`${BASE_URL}/doc-auth/verify/passport`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    IdentityNo: "8601015800086",
    FirstName: "John",
    LastName: "Smith",
    Document1: front,
    Document1Name: "passport_front.jpg",
    ConsentObtainedByDataSubject: true,
  }),
});
const data = await response.json();

console.log(`Result: ${data.verification_result}`);
console.log(`Name: ${data.first_name} ${data.last_name}`);
```

{% endtab %}
{% endtabs %}

***

## Compliance & consent

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

***

## FAQ

<details>

<summary>Which endpoint should I use?</summary>

Choose the endpoint that matches the document type: `/verify/passport` for passports, `/verify/drivers-license` for driver's licenses, and `/verify/national-id` for national ID cards.

</details>

<details>

<summary>Do I need to send both sides of the document?</summary>

Yes, for dual-sided documents like driver's licenses and national ID cards. Passports typically only require the photo page.

</details>

<details>

<summary>What does a `REFER` result mean?</summary>

The service could not confidently accept or reject the document. Route the case to manual review and inspect the extracted fields and image quality before deciding.

</details>

<details>

<summary>How are documents transmitted?</summary>

As base64-encoded strings inside the JSON body. Encode each image client-side before submitting.

</details>

<details>

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

Up to 60 seconds.

</details>

***

## Changelog

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