> For the complete documentation index, see [llms.txt](https://docs.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://docs.fraudcheck.co.za/consumer-service/education/tertiary-verification.md).

# Tertiary Verification

Tertiary Verification confirms a consumer's higher education qualification - degree, diploma, or certificate - against a database of accredited tertiary institutions. Use it for employment screening, professional certification checks, and detecting fraudulent credentials on applications and CVs.

The service is **asynchronous**: the initial request returns immediately with a `PENDING` status and a transaction ID. The final result is produced in the background and retrieved using that transaction ID.

A companion endpoint lists the institutions that can be verified - call it before submitting a request to ensure the `Institution` field will be accepted.

## When to use this

Use this service to:

* Verify a degree or diploma claim during pre-employment screening
* Check professional qualifications for regulated roles
* Detect fraudulent education credentials on CVs and applications
* Confirm bursary or grant eligibility based on qualification

## How it works

Submit the consumer's identity, the institution name, the year the qualification was obtained, and a base64-encoded image of the supporting document (degree certificate, diploma, transcript). The service validates the institution against its active list, immediately accepts the request, and returns a `transaction_id` with `screening_result: "PENDING"`. The actual verification is processed in the background. Once complete, fetch the final result using the transaction endpoint.

| Property              | Value                                                             |
| --------------------- | ----------------------------------------------------------------- |
| Response type         | `Asynchronous`                                                    |
| Typical response time | Immediate ack; final result usually within a few minutes to hours |
| Result retrieval      | Poll `GET /transactions/{transaction_id}`                         |
| Consent required      | `Yes`                                                             |
| Region                | South Africa                                                      |

***

## Authentication

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

***

## Endpoints

```http
POST /tertiary/verify
GET  /tertiary/institutions
```

***

## Listing supported institutions

Before submitting a verification, fetch the list of currently supported institutions. The `Institution` field on the verification request must match the `name` of an `active` entry from this list exactly.

```http
GET /tertiary/institutions
Authorization: Bearer {your_access_token}
```

### Example response

```json
[
  { "name": "University of Cape Town", "status": "active" },
  { "name": "University of the Witwatersrand", "status": "active" },
  { "name": "Stellenbosch University", "status": "active" }
]
```

***

## Submitting a verification

### 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                                                                             |
| `LastName`                     | string  | Yes      | Consumer's surname                                                                                |
| `Institution`                  | string  | Yes      | Name of the tertiary institution. Must match an `active` entry from `GET /tertiary/institutions`. |
| `YearObtained`                 | integer | Yes      | Year the qualification was obtained                                                               |
| `Document1`                    | string  | Yes      | Base64-encoded image or PDF of the supporting document                                            |
| `ConsentObtainedByDataSubject` | boolean | Yes      | Must be `true`. Confirms POPIA consent.                                                           |
| `Document1Name`                | string  | No       | Filename for `Document1` (e.g. `degree_certificate.pdf`)                                          |
| `Qualification`                | string  | No       | Qualification or degree name (e.g. `Bachelor of Science`)                                         |
| `Major`                        | string  | No       | Major or field of study                                                                           |
| `CertificateNo`                | string  | No       | Certificate or diploma number                                                                     |
| `StudentNumber`                | string  | No       | Student number at the institution                                                                 |
| `SecondName`                   | string  | No       | Middle name                                                                                       |
| `BirthDate`                    | string  | No       | Date of birth (`YYYY-MM-DD`). Derived from `IdentityNo` if omitted.                               |

### Document image requirements

* **Formats:** JPEG, PNG, or PDF
* **Encoding:** base64-encoded string in `Document1`
* **Quality:** clear, in-focus, full-colour where possible, with the entire certificate visible

### Example request

```json
{
  "IdentityNo": "8601015800086",
  "FirstName": "John",
  "LastName": "Smith",
  "Institution": "University of Cape Town",
  "YearObtained": 2015,
  "Qualification": "Bachelor of Science",
  "Major": "Computer Science",
  "Document1": "base64_encoded_document_image...",
  "Document1Name": "degree_certificate.pdf",
  "ConsentObtainedByDataSubject": true
}
```

***

## Response (initial acknowledgement)

The initial response confirms the request was accepted. The verification itself runs in the background.

### Response fields

| Field                  | Type   | Description                                                              |
| ---------------------- | ------ | ------------------------------------------------------------------------ |
| `transaction_id`       | string | Unique identifier for this request. Use it to retrieve the final result. |
| `screening_result`     | string | Initial status - always `PENDING` on the first response                  |
| `response_description` | string | Human-readable status message                                            |

### Example response - accepted

```json
{
  "transaction_id": "d41d8cd98f00b204e9800998ecf8427e",
  "screening_result": "PENDING",
  "response_description": "Request accepted and is being processed."
}
```

***

## Retrieving the final result

Once the verification has been processed in the background, fetch the final result using the transaction ID:

```http
GET /transactions/{transaction_id}
Authorization: Bearer {your_access_token}
```

The `screening_result` will transition from `PENDING` to one of:

| Result   | Meaning                                                    |
| -------- | ---------------------------------------------------------- |
| `ACCEPT` | The qualification was verified against institution records |
| `REVIEW` | A partial match was found that requires manual review      |
| `REJECT` | The qualification could not be verified                    |
| `FAILED` | The verification could not be completed                    |

**Recommended polling:** Every 2 hours after 24 four . Tertiary verifications can take longer than other async services because some institutions are slower to respond.

Results are also sent via email

***

## 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 `Institution` not in the active list | See `details.field` or `details.missing_fields`. Re-check the institutions list. |
| 500  | `server_error`  | Unexpected error                                                | Contact support                                                                  |

***

## Code examples

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

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

# 1. List supported institutions
curl -X GET "$BASE_URL/tertiary/institutions" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# 2. Submit a verification
curl -X POST "$BASE_URL/tertiary/verify" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "IdentityNo": "8601015800086",
    "FirstName": "John",
    "LastName": "Smith",
    "Institution": "University of Cape Town",
    "YearObtained": 2015,
    "Qualification": "Bachelor of Science",
    "Document1": "'"$(base64 -w0 degree_certificate.pdf)"'",
    "Document1Name": "degree_certificate.pdf",
    "ConsentObtainedByDataSubject": true
  }'
```

{% endtab %}

{% tab title="Python" %}

```python
import base64
import time
import requests

BASE_URL = "https://consumer-service-api.fraudcheckonline.co.za/consumer-service"
headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json",
}

# 1. Confirm the institution is active
institutions = requests.get(f"{BASE_URL}/tertiary/institutions", headers=headers).json()
active = {i["name"] for i in institutions if i["status"] == "active"}
assert "University of Cape Town" in active

# 2. Encode the supporting document
with open("degree_certificate.pdf", "rb") as f:
    document_b64 = base64.b64encode(f.read()).decode()

# 3. Submit the verification
submit = requests.post(
    f"{BASE_URL}/tertiary/verify",
    headers=headers,
    json={
        "IdentityNo": "8601015800086",
        "FirstName": "John",
        "LastName": "Smith",
        "Institution": "University of Cape Town",
        "YearObtained": 2015,
        "Qualification": "Bachelor of Science",
        "Document1": document_b64,
        "Document1Name": "degree_certificate.pdf",
        "ConsentObtainedByDataSubject": True,
    },
).json()

transaction_id = submit["transaction_id"]
print(f"Submitted, transaction_id={transaction_id}")

# 4. Poll for the final result
for _ in range(20):
    time.sleep(30)
    result = requests.get(f"{BASE_URL}/transactions/{transaction_id}", headers=headers).json()
    if result["screening_result"] != "PENDING":
        print(f"Final result: {result['screening_result']}")
        break
```

{% endtab %}

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

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

const BASE_URL = "https://consumer-service-api.fraudcheckonline.co.za/consumer-service";
const headers = {
  Authorization: `Bearer ${accessToken}`,
  "Content-Type": "application/json",
};

// 1. Encode the document
const document = (await fs.readFile("degree_certificate.pdf")).toString("base64");

// 2. Submit the verification
const submit = await fetch(`${BASE_URL}/tertiary/verify`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    IdentityNo: "8601015800086",
    FirstName: "John",
    LastName: "Smith",
    Institution: "University of Cape Town",
    YearObtained: 2015,
    Qualification: "Bachelor of Science",
    Document1: document,
    Document1Name: "degree_certificate.pdf",
    ConsentObtainedByDataSubject: true,
  }),
});
const { transaction_id } = await submit.json();

// 3. Poll for the final result
for (let i = 0; i < 20; i++) {
  await new Promise(r => setTimeout(r, 30000));
  const res = await fetch(`${BASE_URL}/transactions/${transaction_id}`, { headers });
  const result = await res.json();
  if (result.screening_result !== "PENDING") {
    console.log(`Final result: ${result.screening_result}`);
    break;
  }
}
```

{% 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.
* Store the supporting document securely and retain proof of consent in line with POPIA requirements.

***

## FAQ

<details>

<summary>Why do I need to call `/tertiary/institutions` first?</summary>

The `Institution` field must match an active institution in the database exactly. Calling the institutions endpoint first lets you validate the name (or present a dropdown to your end user) and avoid 422 errors.

</details>

<details>

<summary>Why does verification take longer than other async services?</summary>

Some institutions are slower to respond than others, and verification may involve manual checking against the supporting document. Final results typically arrive within minutes to hours rather than seconds.

</details>

<details>

<summary>What document should I send?</summary>

A clear scan or photograph of the qualification certificate, diploma, or official transcript. Send it base64-encoded in the `Document1` field.

</details>

<details>

<summary>Do I need to provide `BirthDate`?</summary>

No. If omitted, it is derived from the South African ID number.

</details>

<details>

<summary>What does each final result mean?</summary>

`ACCEPT` = qualification verified by the institution. `REVIEW` = partial match needing manual review. `REJECT` = qualification could not be verified. `FAILED` = verification could not complete; retry with a clearer document or contact support.

</details>

***

## Changelog

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


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.fraudcheck.co.za/consumer-service/education/tertiary-verification.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
