> 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/watchlist-verify.md).

# Watchlist

Watchlist screens a person against the watchlists you choose (PEPs, sanctions and crime) in a single call, and can include the person's known relatives. You get a result per watchlist and one overall decision.

## When to use this

Use this service to:

* Screen a person against PEP, sanctions and crime lists with one call
* Choose exactly which lists to run on each request
* Add relatives screening for enhanced due diligence on Politically Exposed Persons

## How it works

1. **Log in** to get a bearer token. See [Authentication](/consumer-service/authentication.md).
2. **Call `POST /watchlist/verify`** with the person's details and the `Watchlists` to run.
3. Every chosen list runs, in your order, for the person, and for each relative when `IncludeRelatives` is `true`.
4. The response returns results per list and an overall `screening_result`. You can fetch it again later with `GET /watchlist/verify/{transaction_id}`.

| Property         | Value                                                                                  |
| ---------------- | -------------------------------------------------------------------------------------- |
| Response type    | `Synchronous`                                                                          |
| Result retrieval | Returned inline; also via `GET /watchlist/verify/{transaction_id}`                     |
| Consent required | `Yes`                                                                                  |
| Billing          | One transaction per call, whatever lists are chosen and whether relatives are included |

***

## Endpoints

```http
GET  /watchlist/verify                     (list catalogue)
POST /watchlist/verify
GET  /watchlist/verify/{transaction_id}
```

## List catalogue

`GET /watchlist/verify` returns the lists searched by each `Watchlists` option, with their codes and titles. Use the codes in `Filters.ListCodes` to focus a search on specific lists. It requires an access token and is limited to 20 requests per minute.

```http
GET /watchlist/verify
Authorization: Bearer {your_access_token}
```

```json
{
  "watchlists": [
    {
      "watchlist": "peps",
      "lists": [
        { "code": "za_mm_officials", "title": "South Africa Municipal Leadership" },
        { "code": "za_pmg_legislators", "title": "South Africa National and Provincial Legislators" }
      ]
    },
    {
      "watchlist": "sanctions",
      "lists": [
        { "code": "un_sc_sanctions", "title": "UN Security Council Consolidated Sanctions" },
        { "code": "za_fic_sanctions", "title": "South Africa Targeted Financial Sanctions" }
      ]
    },
    {
      "watchlist": "crime",
      "lists": [
        { "code": "interpol_red_notices", "title": "INTERPOL Red Notices" },
        { "code": "za_wanted", "title": "South Africa Wanted Persons" }
      ]
    }
  ]
}
```

The example is shortened; the live response lists every list.

## Request

### Headers

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

### Body parameters

| Field                          | Type    | Required | Default                 | Description                                                                                                                                                                                       |
| ------------------------------ | ------- | -------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FirstName`                    | string  | Yes      |                         | Person's first name                                                                                                                                                                               |
| `LastName`                     | string  | Yes      |                         | Person's surname                                                                                                                                                                                  |
| `IdentityNo`                   | string  | Yes      |                         | Person's identity number                                                                                                                                                                          |
| `ConsentObtainedByDataSubject` | boolean | Yes      |                         | Must be `true`. Confirms POPIA consent.                                                                                                                                                           |
| `SecondName`                   | string  | No       |                         | Middle name. Improves match accuracy.                                                                                                                                                             |
| `Watchlists`                   | array   | No       | `["peps", "sanctions"]` | Lists to run, in order. See [Watchlists](#watchlists).                                                                                                                                            |
| `IncludeRelatives`             | boolean | No       | `false`                 | Also screen the person's known relatives against the same lists.                                                                                                                                  |
| `Filters`                      | object  | No       |                         | Person's date of birth, gender and countries, used to remove matches that clearly belong to someone else. See [Filters](#filters).                                                                |
| `case_id`                      | string  | No       |                         | A case from [Case Management](/consumer-service/reference/case-management.md). With `entity_id`, the result is also logged on that case. See [Log the check on a case](#log-the-check-on-a-case). |
| `entity_id`                    | string  | No       |                         | The case's subject. Send it with `case_id`.                                                                                                                                                       |

### Watchlists

Choose 1 to 3 lists, each at most once. They run in the order you give.

| Value       | Lists screened                                                                                                                                     |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `peps`      | Politically Exposed Persons lists, e.g. South African legislators and municipal leadership                                                         |
| `sanctions` | Financial sanctions (e.g. FIC, UN Security Council, OFAC, EU, Swiss SECO) and regulatory / debarment lists (e.g. US SAM, African Development Bank) |
| `crime`     | Wanted-person lists, e.g. INTERPOL Red Notices and South Africa Wanted Persons                                                                     |

Every list belongs to exactly one option, so the same listing never appears under two options. See the [list catalogue](#list-catalogue) for the exact lists in each.

### Filters

Filters remove a match only when the watchlist record **clearly belongs to someone else**: both the person and the record have a value, and they disagree. If the record has no date of birth, gender or country, the match is always kept.

| Field         | Type   | Accepted formats                                                                                      | A match is removed when                            |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| `DateOfBirth` | string | `1995-09-03`, `03/09/1995`, `1995/09/03`, or `1995`                                                   | The record's birth year(s) don't include this year |
| `Gender`      | string | `M`, `F`, `male`, `female`                                                                            | The record lists only the other gender             |
| `Countries`   | array  | Country codes (`ZA`, `ZAF`) or names (`South Africa`)                                                 | None of the record's countries are in this list    |
| `ListCodes`   | array  | List codes from [the list catalogue](#list-catalogue), e.g. `["za_fic_sanctions", "un_sc_sanctions"]` | Only these lists are searched (see below)          |

`ListCodes` focuses the search on specific lists for everyone screened, including relatives. Each chosen `Watchlists` option searches only the requested codes it contains; an option that contains none of them returns no matches. A code that none of the chosen options contains returns HTTP `422` with `unknown_list_codes` and `allowed_list_codes`, before any charge.

If `IdentityNo` is a valid South African ID number, the date of birth and gender are read from it automatically when you don't send them. Any other identifier, such as a passport number, is not used. Country is only used when you send it.

Filters apply to the person only. Relatives are never filtered with the person's details. Removed matches are listed in `filtered_out` so you can see what was excluded and why.

### Example request

```json
{
  "FirstName": "Jane",
  "LastName": "Smith",
  "IdentityNo": "*************",
  "ConsentObtainedByDataSubject": true,
  "Watchlists": ["peps", "sanctions"],
  "IncludeRelatives": false
}
```

### Example request (with filters)

```json
{
  "FirstName": "Jane",
  "LastName": "Smith",
  "IdentityNo": "A12345678",
  "ConsentObtainedByDataSubject": true,
  "Watchlists": ["peps", "sanctions", "crime"],
  "Filters": {
    "DateOfBirth": "1995-09-03",
    "Gender": "F",
    "Countries": ["ZA"],
    "ListCodes": ["za_fic_sanctions", "un_sc_sanctions", "za_pmg_legislators"]
  }
}
```

***

## Response

### Response fields

| Field              | Type           | Description                                                                                                                                                                             |
| ------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `transaction_id`   | string         | Unique request identifier                                                                                                                                                               |
| `product`          | string         | `WATCHLISTVERIFY`                                                                                                                                                                       |
| `screening_result` | string         | Overall decision: the worst result across everyone screened (see below)                                                                                                                 |
| `watchlists`       | array          | The lists that ran, in order                                                                                                                                                            |
| `relatives_status` | string         | `not_requested` when `IncludeRelatives` is `false`; otherwise see below                                                                                                                 |
| `filters_applied`  | object \| null | The normalised `date_of_birth`, `gender`, `countries` and `list_codes` used, and `derived_from_identity_number` listing any taken from the ID number. `null` when no filters were used. |
| `subject`          | object         | Result for the person                                                                                                                                                                   |
| `relatives`        | array          | One result per relative. Empty when `IncludeRelatives` is `false`.                                                                                                                      |
| `summary`          | object         | `people_screened`, `people_matched`, `total_matches`                                                                                                                                    |

Each person result (`subject` and each entry in `relatives`) has:

| Field              | Type   | Description                                                                                              |
| ------------------ | ------ | -------------------------------------------------------------------------------------------------------- |
| `name`             | string | Name that was screened                                                                                   |
| `relationship`     | string | **Relatives only.** `spouse`, `partner`, `father`, `mother`, `parent`, `child`, `sibling`, or `relative` |
| `screening_result` | string | `ACCEPT`, `REVIEW`, or `REJECT` for this person                                                          |
| `top_score`        | number | Highest match percentage across this person's lists                                                      |
| `lists`            | array  | One entry per list: `watchlist`, `match_count`, `top_score`, `matches`, `filtered_out`                   |

Each item in `matches` has the same fields as a [Watchlist Screening](/consumer-service/aml-and-watchlist/watchlist-screening.md) match, except `source_table`, plus `gender` where the record has one. Use `dataset_code` and `dataset_title` to see which list it came from. Each item in `filtered_out` has `name`, `source_entity_id`, `match_percentage` and `reasons` (`birth_year`, `gender`, `country`). Removed matches don't count towards `match_count`, `top_score` or the decision.

### Screening results

| Result   | Meaning                                        |
| -------- | ---------------------------------------------- |
| `ACCEPT` | No matches                                     |
| `REVIEW` | At least one match, with a top score below 75% |
| `REJECT` | A match of 75% or more                         |

The overall `screening_result` is the most serious result across the person and any relatives (`REJECT`, then `REVIEW`, then `ACCEPT`).

### Relatives status

| Value                   | Meaning                                                                                             |
| ----------------------- | --------------------------------------------------------------------------------------------------- |
| `not_requested`         | `IncludeRelatives` was `false`                                                                      |
| `found`                 | Relatives were found and screened                                                                   |
| `none_found`            | No recorded relatives for this person                                                               |
| `identity_not_resolved` | Several people match this name, so relatives could not be identified. The person is still screened. |
| `unavailable`           | The relatives lookup is temporarily unavailable. The person is still screened.                      |

{% hint style="info" %}
Relatives come from public records of notable people, such as politicians and public officials. Most private individuals have no recorded relatives. Always confirm a relative's identity before acting on a match.
{% endhint %}

### Example response

```json
{
  "transaction_id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "product": "WATCHLISTVERIFY",
  "screening_result": "REVIEW",
  "watchlists": ["peps", "sanctions"],
  "relatives_status": "not_requested",
  "filters_applied": null,
  "subject": {
    "name": "Jane Smith",
    "screening_result": "REVIEW",
    "top_score": 72.0,
    "lists": [
      { "watchlist": "peps", "match_count": 0, "top_score": 0.0, "matches": [], "filtered_out": [] },
      {
        "watchlist": "sanctions",
        "match_count": 1,
        "top_score": 72.0,
        "matches": [
          {
            "dataset_title": "South Africa Targeted Financial Sanctions",
            "name": "Jane Smyth",
            "match_percentage": 72.0,
            "matched_via_alias": false
          }
        ],
        "filtered_out": []
      }
    ]
  },
  "relatives": [],
  "summary": { "people_screened": 1, "people_matched": 1, "total_matches": 1 }
}
```

Match objects are shortened in this example.

***

## Log the check on a case

If you use [Case Management](/consumer-service/reference/case-management.md), send the case's `case_id` and its subject's `entity_id` with the request. The check runs, is billed and answers exactly as without them. After you have the response, the result is also logged on the case as a **point verification**.

```json
{
  "FirstName": "Jane",
  "LastName": "Smith",
  "IdentityNo": "8601015800086",
  "ConsentObtainedByDataSubject": true,
  "case_id": "c7e1b2a4-2222-4ccc-8ddd-000000000002",
  "entity_id": "a3f2c1d0-1111-4aaa-8bbb-000000000001"
}
```

| Rule                             | What happens                                                                                                               |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| The response                     | Unchanged. It doesn't say whether the check was logged                                                                     |
| The case                         | The check is listed with the case's runs, with its result and report. Its `transaction_id` is the check's `transaction_id` |
| The case's status                | Never changed by a point verification. The case still follows its workflow runs, or your own resolution                    |
| A resolved case                  | The check isn't logged on it                                                                                               |
| A wrong `case_id` or `entity_id` | The check still runs and answers. It isn't logged on any case                                                              |

To see it, get the case with [`GET /case-management/cases/{case_id}`](/consumer-service/reference/case-management.md#the-case): it is the run with `run_kind` `point_verification`.

***

## Retrieving a result

```http
GET /watchlist/verify/{transaction_id}
Authorization: Bearer {your_access_token}
```

Returns the same response as the original request, exactly as it was found at the time of screening.

***

## Errors

| HTTP | Meaning                                                                                                                                      | Action                               |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| 422  | Required field missing, consent not `true`, an unknown or repeated `Watchlists` value, or a `ListCodes` value the chosen lists don't contain | Check `detail`                       |
| 429  | More than 20 requests per minute to the list catalogue                                                                                       | Slow down and retry                  |
| 401  | Token missing or expired                                                                                                                     | Refresh your access token            |
| 403  | Account lacks access to this service, or the transaction belongs to another account                                                          | Contact your account manager         |
| 404  | `transaction_id` not found                                                                                                                   | Confirm the transaction ID           |
| 503  | The watchlist service is temporarily unavailable                                                                                             | Retry; contact support if persistent |

{% hint style="info" %}
A match is not an error. Matches return HTTP `200` with `REVIEW` or `REJECT`.
{% endhint %}

***

## Code examples

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

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

curl -X POST "$BASE_URL/watchlist/verify" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "FirstName": "Jane",
    "LastName": "Smith",
    "IdentityNo": "*************",
    "ConsentObtainedByDataSubject": true,
    "Watchlists": ["peps", "sanctions"],
    "IncludeRelatives": false
  }'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

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

result = requests.post(
    f"{BASE_URL}/watchlist/verify",
    headers={"Authorization": f"Bearer {access_token}"},
    json={
        "FirstName": "Jane",
        "LastName": "Smith",
        "IdentityNo": "*************",
        "ConsentObtainedByDataSubject": True,
        "Watchlists": ["peps", "sanctions"],
        "IncludeRelatives": False,
    },
).json()

print("Overall:", result["screening_result"])
for entry in result["subject"]["lists"]:
    print(entry["watchlist"], entry["match_count"], entry["top_score"])
```

{% endtab %}

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

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

const result = await (
  await fetch(`${BASE_URL}/watchlist/verify`, {
    method: "POST",
    headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      FirstName: "Jane",
      LastName: "Smith",
      IdentityNo: "*************",
      ConsentObtainedByDataSubject: true,
      Watchlists: ["peps", "sanctions"],
      IncludeRelatives: false,
    }),
  })
).json();

console.log("Overall:", result.screening_result);
```

{% 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.
* Treat a match, especially a relative's, as an indicator for further due diligence, not as a confirmed identity.

***

## Changelog

| Date       | Version | Change                                                                      |
| ---------- | ------- | --------------------------------------------------------------------------- |
| 2026-11-03 | v1.1    | `case_id` and `entity_id`: log the check on a case as a point verification. |
| 2026-10-22 | 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/watchlist-verify.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.
