> 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/aml-and-watchlist/safps-fraud-listing-lookup.md).

# SAFPS Fraud Listing Lookup

SAFPS Fraud Listing Lookup checks whether a South African consumer is recorded on the SAFPS (South African Fraud Prevention Service) fraud database. Use it to flag known fraud listings during onboarding, employment screening, or credit applications.

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.

## When to use this

Use this service to:

* Screen consumers against SAFPS fraud listings during onboarding
* Run pre-employment fraud checks
* Detect fraud risk on credit applications
* Support KYC and AML workflows that require a SAFPS check

## How it works

Submit the consumer's name, ID number, and consent flag in a single POST request. The service immediately accepts the request, creates a tracking transaction, and returns a `transaction_id` with `screening_result: "PENDING"`. The actual SAFPS lookup 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 1–5 minutes |
| Result retrieval      | Poll `GET /transactions/{transaction_id}`              |
| Consent required      | `Yes`                                                  |
| Region                | South Africa                                           |

***

## Authentication

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

***

## Endpoint

```http
POST /fraud-listing-lookup/verify
```

***

## 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                                               |
| `LastName`                     | string  | Yes      | Consumer's surname                                                  |
| `ConsentObtainedByDataSubject` | boolean | Yes      | Must be `true`. Confirms POPIA consent.                             |
| `SecondName`                   | string  | No       | Middle name                                                         |
| `BirthDate`                    | string  | No       | Date of birth (`YYYY-MM-DD`). Derived from `IdentityNo` if omitted. |
| `ReferenceNo`                  | string  | No       | Your internal reference, echoed back in result lookups              |

### Example request

```json
{
  "IdentityNo": "8601015800086",
  "FirstName": "John",
  "LastName": "Smith",
  "ConsentObtainedByDataSubject": true,
  "ReferenceNo": "ORDER-12345"
}
```

***

## Response (initial acknowledgement)

The initial response confirms the request was accepted. The screening 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 lookup 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` | No SAFPS fraud listing found for this consumer         |
| `REVIEW` | A possible match was found that requires manual review |
| `REJECT` | A confirmed SAFPS fraud listing was found              |
| `FAILED` | The lookup could not be completed                      |

**Recommended polling:** every 5 seconds, up to 2 minutes. If the result is still `PENDING` after 2 minutes, switch to a longer interval with exponential backoff.

***

## 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.missing_fields` |
| 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"

curl -X POST "$BASE_URL/fraud-listing-lookup/verify" \
  -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 time
import requests

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

# Submit the request
response = requests.post(
    f"{BASE_URL}/fraud-listing-lookup/verify",
    headers=headers,
    json={
        "IdentityNo": "8601015800086",
        "FirstName": "John",
        "LastName": "Smith",
        "ConsentObtainedByDataSubject": True,
    },
)
transaction_id = response.json()["transaction_id"]
print(f"Submitted, transaction_id={transaction_id}")

# Poll for the final result
for _ in range(24):  # up to 2 minutes
    time.sleep(5)
    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
const BASE_URL = "https://consumer-service-api.fraudcheckonline.co.za/consumer-service";
const headers = {
  Authorization: `Bearer ${accessToken}`,
  "Content-Type": "application/json",
};

// Submit the request
const submit = await fetch(`${BASE_URL}/fraud-listing-lookup/verify`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    IdentityNo: "8601015800086",
    FirstName: "John",
    LastName: "Smith",
    ConsentObtainedByDataSubject: true,
  }),
});
const { transaction_id } = await submit.json();

// Poll for the final result
for (let i = 0; i < 24; i++) {
  await new Promise(r => setTimeout(r, 5000));
  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.
* Retain proof of consent and the `transaction_id` for each lookup in line with POPIA requirements.

***

## FAQ

<details>

<summary>Why is the result <code>PENDING</code> when I first call the API?</summary>

This service is asynchronous. The initial response confirms the request was accepted; the actual SAFPS lookup runs in the background and the result becomes available shortly afterwards.

</details>

<details>

<summary>How do I get the final result?</summary>

Poll `GET /transactions/{transaction_id}` until `screening_result` is no longer `PENDING`. A 5-second interval for up to 2 minutes is a reasonable starting point.

</details>

<details>

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

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

</details>

<details>

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

`ACCEPT` = no listing found. `REVIEW` = possible match needing manual review. `REJECT` = confirmed SAFPS fraud listing. `FAILED` = the lookup could not complete; retry the request.

</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/aml-and-watchlist/safps-fraud-listing-lookup.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.
