> 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/credit-and-financial-risk/credit-score.md).

# Credit Score

Credit Score returns a risk assessment for a South African consumer, combining credit bureau data with fraud indicators to produce a single classification (`ACCEPT` / `REVIEW` / `REJECT`) and a numeric score from 0–999. Use it as the quick decisioning input during onboarding, loan origination, and account opening.

The service returns:

* A **final score** (0–999) and risk category (`GREEN` / `AMBER` / `RED`)
* A **classification** suitable for automated decisioning
* **Fraud indicators** - SAFPS listing, deceased status, employer fraud, protective verification
* **Adverse information** - defaults, judgements, debt review

## When to use this

Use this service to:

* Make a single accept/review/reject decision during consumer onboarding
* Score loan applicants and account opening requests
* Combine credit and fraud risk into one decisioning step
* Replace multiple individual checks (SAFPS, defaults, judgements, deceased status) with one call

## How it works

The service is a **two-step call**. The first request submits the consumer's identity and returns a transaction ID along with enquiry identifiers. The second request uses those identifiers to fetch the full credit score report. Both calls are synchronous.

| Property              | Value                          |
| --------------------- | ------------------------------ |
| Response type         | `Synchronous` (two-step)       |
| Typical response time | 2–5s per call                  |
| Result retrieval      | Step 2 returns the full report |
| Consent required      | `Yes`                          |
| Region                | South Africa                   |

***

## Authentication

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

***

## Endpoints

```http
POST /prevet          # Step 1: submit consumer
POST /prevet/result   # Step 2: fetch full report
```

***

## Step 1 - Submit consumer

### 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                                                 |
| `PrescribedPurpose`                | string  | Yes      | Purpose of the enquiry (e.g. `Account Opening`, `Loan Assessment`) |
| `ConsentObtainedByDataSubject`     | boolean | Yes      | Must be `true`. Confirms POPIA consent.                            |
| `SecondName`, `BirthDate`, `Sex`   | string  | No       | Additional identity fields                                         |
| `CellNo`, `EmailAddress`           | string  | No       | Contact details                                                    |
| `Employer`, `Occupation`, `Salary` | string  | No       | Employment details (improve fraud signals)                         |

### Example request

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

### Example response

```json
{
  "transaction_id": "TXN20240119ABC123XYZ",
  "identifier": "8601015800086",
  "first_name": "JOHN",
  "match": {
    "ListOfConsumers": [
      {
        "ConsumerID": "123456",
        "FirstName": "JOHN",
        "Surname": "SMITH",
        "IDNo": "8601015800086",
        "BirthDate": "1986-01-15",
        "EnquiryID": "ENQ12345",
        "EnquiryResultID": "RES67890"
      }
    ]
  }
}
```

Save the `transaction_id`, `EnquiryID`, and `EnquiryResultID` - you'll need them for step 2.

***

## Step 2 - Fetch full report

### Body parameters

| Field             | Type   | Required | Description                                     |
| ----------------- | ------ | -------- | ----------------------------------------------- |
| `transaction_id`  | string | Yes      | From step 1                                     |
| `EnquiryID`       | string | Yes      | From `match.ListOfConsumers[0].EnquiryID`       |
| `EnquiryResultID` | string | Yes      | From `match.ListOfConsumers[0].EnquiryResultID` |

### Example request

```json
{
  "transaction_id": "TXN20240119ABC123XYZ",
  "EnquiryID": "ENQ12345",
  "EnquiryResultID": "RES67890"
}
```

### Response - full report

The full report is returned under a top-level `Consumer` object with the following sub-sections:

| Section                                   | Description                                                              |
| ----------------------------------------- | ------------------------------------------------------------------------ |
| `Consumer.ConsumerDetail`                 | Demographics and identity                                                |
| `Consumer.ConsumerScoring`                | Final score, classification, and risk category                           |
| `Consumer.ConsumerFraudIndicatorsSummary` | Fraud signals (SAFPS, deceased, employer fraud, protective verification) |
| `Consumer.AdverseInfoSummary`             | Defaults, judgements, debt review                                        |

#### `ConsumerScoring` fields

| Field            | Type    | Description                                               |
| ---------------- | ------- | --------------------------------------------------------- |
| `FinalScore`     | integer | Risk score from `0` (lowest risk) to `999` (highest risk) |
| `RiskCategory`   | string  | `GREEN`, `AMBER`, or `RED`                                |
| `classification` | string  | `ACCEPT`, `REVIEW`, or `REJECT`                           |

#### `ConsumerFraudIndicatorsSummary` fields

| Field                         | `Y` means                                                     |
| ----------------------------- | ------------------------------------------------------------- |
| `SAFPSListingYN`              | Listed on the South African Fraud Prevention Service database |
| `HomeAffairsDeceasedStatus`   | Marked deceased on Home Affairs records                       |
| `EmployerFraudVerificationYN` | Employer details match a known fraud pattern                  |
| `ProtectiveVerificationYN`    | Subject has a protective registration on file                 |

#### `AdverseInfoSummary` fields

| Field                     | `Y` means                          |
| ------------------------- | ---------------------------------- |
| `XDSDefaultListingStatus` | Has payment default(s) on file     |
| `JudgementStatus`         | Has a legal judgement against them |
| `DebtReviewStatus`        | Currently under debt review        |

### Example response (abbreviated)

```json
{
  "Consumer": {
    "ConsumerDetail": {
      "FirstName": "JOHN",
      "Surname": "SMITH",
      "IDNo": "8601015800086",
      "BirthDate": "1986-01-15"
    },
    "ConsumerScoring": {
      "FinalScore": 245,
      "RiskCategory": "GREEN",
      "classification": "ACCEPT"
    },
    "ConsumerFraudIndicatorsSummary": {
      "SAFPSListingYN": "N",
      "HomeAffairsDeceasedStatus": "Alive",
      "EmployerFraudVerificationYN": "N",
      "ProtectiveVerificationYN": "N"
    },
    "AdverseInfoSummary": {
      "XDSDefaultListingStatus": "N",
      "JudgementStatus": "N",
      "DebtReviewStatus": "N"
    }
  }
}
```

***

## Decisioning at a glance

| `classification` | `RiskCategory` | Recommended action                                                |
| ---------------- | -------------- | ----------------------------------------------------------------- |
| `ACCEPT`         | `GREEN`        | Proceed with normal flow                                          |
| `REVIEW`         | `AMBER`        | Route to manual review; inspect fraud indicators and adverse info |
| `REJECT`         | `RED`          | Decline the application                                           |

A `REJECT` classification or any `Y` flag in `ConsumerFraudIndicatorsSummary` should be treated as a strong fraud or risk signal regardless of the numeric score.

***

## Errors

| HTTP | Code           | Meaning                                      | Action                                    |
| ---- | -------------- | -------------------------------------------- | ----------------------------------------- |
| 400  | `bad_request`  | Required field missing or consent not `true` | Provide all required fields               |
| 401  | `unauthorized` | Token missing or expired                     | Refresh your access token                 |
| 403  | `forbidden`    | Account lacks access to this service         | Contact your account manager              |
| 500  | `server_error` | Unexpected error                             | Contact support with the `transaction_id` |

***

## Code examples

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

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

# Step 1
curl -X POST "$BASE_URL/prevet" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "IdentityNo": "8601015800086",
    "FirstName": "John",
    "LastName": "Smith",
    "PrescribedPurpose": "Account Opening",
    "ConsentObtainedByDataSubject": true
  }'

# Step 2
curl -X POST "$BASE_URL/prevet/result" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "TXN20240119ABC123XYZ",
    "EnquiryID": "ENQ12345",
    "EnquiryResultID": "RES67890"
  }'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

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

# Step 1 - submit consumer
step1 = requests.post(
    f"{BASE_URL}/prevet",
    headers=headers,
    json={
        "IdentityNo": "8601015800086",
        "FirstName": "John",
        "LastName": "Smith",
        "PrescribedPurpose": "Account Opening",
        "ConsentObtainedByDataSubject": True,
    },
).json()

transaction_id = step1["transaction_id"]
consumer = step1["match"]["ListOfConsumers"][0]

# Step 2 - fetch full report
report = requests.post(
    f"{BASE_URL}/prevet/result",
    headers=headers,
    json={
        "transaction_id": transaction_id,
        "EnquiryID": consumer["EnquiryID"],
        "EnquiryResultID": consumer["EnquiryResultID"],
    },
).json()

scoring = report["Consumer"]["ConsumerScoring"]
fraud = report["Consumer"]["ConsumerFraudIndicatorsSummary"]

print(f"Score: {scoring['FinalScore']}/999 ({scoring['RiskCategory']})")
print(f"Classification: {scoring['classification']}")

if fraud["SAFPSListingYN"] == "Y":
    print("⚠️  SAFPS listing on file")
if fraud["HomeAffairsDeceasedStatus"] != "Alive":
    print("⚠️  Deceased status flagged")
```

{% endtab %}

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

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

// Step 1
const step1 = await fetch(`${BASE_URL}/prevet`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    IdentityNo: "8601015800086",
    FirstName: "John",
    LastName: "Smith",
    PrescribedPurpose: "Account Opening",
    ConsentObtainedByDataSubject: true,
  }),
}).then(r => r.json());

const consumer = step1.match.ListOfConsumers[0];

// Step 2
const report = await fetch(`${BASE_URL}/prevet/result`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    transaction_id: step1.transaction_id,
    EnquiryID: consumer.EnquiryID,
    EnquiryResultID: consumer.EnquiryResultID,
  }),
}).then(r => r.json());

const { FinalScore, RiskCategory, classification } = report.Consumer.ConsumerScoring;
console.log(`${classification} - score ${FinalScore}/999 (${RiskCategory})`);
```

{% 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.
* Always supply a valid `PrescribedPurpose` matching the actual reason for the check.
* Retain proof of consent and the `transaction_id` for each enquiry in line with NCA and POPIA requirements.

***

## FAQ

<details>

<summary>Why is this a two-step call?</summary>

Step 1 matches the consumer and returns enquiry identifiers; step 2 uses those identifiers to retrieve the full scored report. Both calls happen synchronously and typically complete in a few seconds total.

</details>

<details>

<summary>How does the score relate to the classification?</summary>

`FinalScore` is a continuous 0–999 risk value. `RiskCategory` buckets it into `GREEN` / `AMBER` / `RED`, and `classification` translates that into an action (`ACCEPT` / `REVIEW` / `REJECT`). Use `classification` for automated decisioning and the underlying score and indicators when you need to explain the decision.

</details>

<details>

<summary>Higher score = higher or lower risk?</summary>

Higher = more risk. A score near `0` is the lowest-risk consumer; near `999` is the highest-risk.

</details>

<details>

<summary>What's the difference between this and the [Credit Check](credit-check.md) service?</summary>

Credit Check returns the raw bureau profile (accounts, balances, payment history). Credit Score adds fraud indicators on top and produces a single decision-ready classification. Use Credit Score for automated onboarding decisions; use Credit Check when you need the underlying account-level detail.

</details>

<details>

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

Allow up to 30 seconds per call, 60 seconds total for both steps combined.

</details>

***

## Changelog

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