KYC Verification

ZB ID provides a central KYC module that other ZB services call to validate national IDs against the e-Gov registry, run FCB credit bureau searches, and screen individuals and organisations against World-Check AML watchlists. Every verification is persisted as an auditable record.


Overview

The KYC endpoints live under /v1/kyc. Each one requires a specific scope on the calling client's token:

EndpointMethodScope
/v1/kyc/validate-idPOSTkyc:validate
/v1/kyc/credit-checkPOSTkyc:credit-check
/v1/kyc/screen-individualPOSTkyc:screen
/v1/kyc/screen-corporatePOSTkyc:screen
/v1/kyc/screen-resultPOSTkyc:screen
/v1/kyc/screen-toolkitGETkyc:screen
/v1/kyc/screen-resolvePOSTkyc:screen
/v1/kyc/verifications/{id}GETkyc:screen
/v1/kyc/verificationsGETkyc:screen

Two behaviours reduce duplicate provider calls:

  • National ID validation is cached for 30 days per national ID. A repeat validation within that window returns the stored record with cacheHit: true.
  • Watchlist screening is deduplicated. An individual is matched by national ID or by a normalized fullName plus date of birth; an organisation by its normalized name. A matching prior case is reused with cacheHit: true instead of opening a new one.

Credit checks are never cached; each request calls the bureau fresh.


The verification object

Most KYC endpoints return a verification object describing the persisted record.

  • Name
    id
    Type
    string (UUID)
    Description

    Unique identifier for this verification record.

  • Name
    userId
    Type
    string (UUID) | null
    Description

    The linked user, if one was supplied. null for pre-registration checks.

  • Name
    nationalId
    Type
    string | null
    Description

    The national ID for individuals, or the normalized identity key used for watchlist deduplication.

  • Name
    type
    Type
    string
    Description

    One of EGOV, FCB, or WORLDCHECK.

  • Name
    status
    Type
    string
    Description

    One of PENDING, VERIFIED, REJECTED, COMPLETED, or FAILED.

  • Name
    statusReason
    Type
    string | null
    Description

    A human-readable reason when the verification was rejected or failed.

  • Name
    externalRef
    Type
    string | null
    Description

    The provider's reference for a watchlist case, when one exists.

  • Name
    cacheHit
    Type
    boolean
    Description

    true when the record was served from cache or reused via deduplication rather than a fresh provider call.

  • Name
    requestSnapshot
    Type
    object | null
    Description

    The request payload sent to the provider, minus any injected credentials.

  • Name
    responsePayload
    Type
    object | null
    Description

    The provider's response, as stored.

  • Name
    createdAt
    Type
    string (ISO 8601)
    Description

    When the record was created.

Verification object

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "nationalId": "63-123456A78",
  "type": "EGOV",
  "status": "VERIFIED",
  "statusReason": null,
  "externalRef": null,
  "cacheHit": false,
  "requestSnapshot": {
    "nationalId": "63-123456A78"
  },
  "responsePayload": {
    "Status": "A"
  },
  "createdAt": "2026-07-10T09:12:00Z"
}

POST/v1/kyc/validate-id

Validate a national ID

Validate a national ID against the e-Gov registry. Requires the kyc:validate scope. Results are cached for 30 days per national ID. When the ID is active and the linked user is currently at KYC tier NONE, the user's tier is raised to BASIC.

Request body

  • Name
    nationalId
    Type
    string
    Description

    The national ID to validate. Up to 20 characters.

  • Name
    userId
    Type
    string (UUID)
    Description

    Optional. Link this verification to an existing user so a successful check can raise their KYC tier.

An active ID returns status: "VERIFIED". An ID belonging to a deceased person, or one that is not active in the registry, returns status: "REJECTED" with a statusReason.

Request

POST
/v1/kyc/validate-id
curl -X POST https://id.zb.co.zw/v1/kyc/validate-id \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "nationalId": "63-123456A78",
    "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }'

Response

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "nationalId": "63-123456A78",
  "type": "EGOV",
  "status": "VERIFIED",
  "statusReason": null,
  "externalRef": null,
  "cacheHit": false,
  "requestSnapshot": { "nationalId": "63-123456A78" },
  "responsePayload": { "Status": "A" },
  "createdAt": "2026-07-10T09:12:00Z"
}

POST/v1/kyc/credit-check

Run a credit check

Run an FCB credit bureau search. Requires the kyc:credit-check scope. This endpoint always calls the bureau fresh; results are never cached. Bureau credentials are injected server-side and are never part of the request or the stored snapshot.

Request body

  • Name
    dob
    Type
    string
    Description

    Date of birth, YYYY-MM-DD.

  • Name
    names
    Type
    string
    Description

    The subject's given names.

  • Name
    surname
    Type
    string
    Description

    The subject's surname.

  • Name
    nationalId
    Type
    string
    Description

    National ID, up to 20 characters.

  • Name
    gender
    Type
    string
    Description

    M or F.

  • Name
    searchPurpose
    Type
    string
    Description

    The reason for the search.

  • Name
    married
    Type
    string
    Description

    Marital status.

  • Name
    nationality
    Type
    string
    Description

    Nationality.

  • Name
    streetno
    Type
    string
    Description

    Street number.

  • Name
    streetname
    Type
    string
    Description

    Street name.

  • Name
    suburb
    Type
    string
    Description

    Suburb.

  • Name
    city
    Type
    string
    Description

    City.

  • Name
    mobile
    Type
    string
    Description

    Mobile number.

  • Name
    propertyDensity
    Type
    string
    Description

    Property density.

  • Name
    propertyStatus
    Type
    string
    Description

    Property status.

  • Name
    occupationClass
    Type
    string
    Description

    Occupation class.

  • Name
    employer
    Type
    string
    Description

    Employer.

  • Name
    employerIndustry
    Type
    string
    Description

    Employer industry.

  • Name
    salaryBand
    Type
    string
    Description

    Salary band.

  • Name
    loanPurpose
    Type
    string
    Description

    Loan purpose.

  • Name
    loanAmount
    Type
    string
    Description

    Loan amount.

  • Name
    driversLicence
    Type
    string
    Description

    Optional. Driver's licence number.

  • Name
    passport
    Type
    string
    Description

    Optional. Passport number.

  • Name
    building
    Type
    string
    Description

    Optional. Building name.

  • Name
    pbag
    Type
    string
    Description

    Optional. Private bag.

  • Name
    telephone
    Type
    string
    Description

    Optional. Landline number.

  • Name
    indEmail
    Type
    string
    Description

    Optional. The subject's email address.

  • Name
    userId
    Type
    string (UUID)
    Description

    Optional. Link the verification to an existing user.

A successful bureau response returns status: "COMPLETED"; any other bureau code, or a provider error, returns status: "FAILED".

Request

POST
/v1/kyc/credit-check
curl -X POST https://id.zb.co.zw/v1/kyc/credit-check \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "dob": "1990-01-15",
    "names": "John",
    "surname": "Moyo",
    "nationalId": "63-123456A78",
    "gender": "M",
    "searchPurpose": "1",
    "married": "1",
    "nationality": "1",
    "streetno": "12",
    "streetname": "Samora Machel Ave",
    "suburb": "Avondale",
    "city": "Harare",
    "mobile": "+263771234567",
    "propertyDensity": "1",
    "propertyStatus": "1",
    "occupationClass": "1",
    "employer": "Example Ltd",
    "employerIndustry": "1",
    "salaryBand": "3",
    "loanPurpose": "1",
    "loanAmount": "5000"
  }'

Response

{
  "id": "0d3f5a9e-1c22-4b7a-9d2e-7a6f8b1c2d34",
  "userId": null,
  "nationalId": "63-123456A78",
  "type": "FCB",
  "status": "COMPLETED",
  "statusReason": null,
  "externalRef": null,
  "cacheHit": false,
  "createdAt": "2026-07-10T09:20:00Z"
}

POST/v1/kyc/screen-individual

Screen an individual

Open a World-Check AML screening case for an individual. Requires the kyc:screen scope. If a case already exists for the same national ID, or the same normalized fullName plus date of birth, that case is reused (cacheHit: true).

Request body

  • Name
    fullName
    Type
    string
    Description

    The person's full name. Up to 80 characters.

  • Name
    dateOfBirth
    Type
    string
    Description

    Date of birth, YYYY-MM-DD.

  • Name
    gender
    Type
    string
    Description

    MALE or FEMALE.

  • Name
    nationality
    Type
    string
    Description

    ISO 3166-1 alpha-2 country code (for example, ZW).

  • Name
    nationalId
    Type
    string
    Description

    Optional. National ID, up to 20 characters. Improves deduplication.

  • Name
    userId
    Type
    string (UUID)
    Description

    Optional. Link the verification to an existing user.

A newly opened case is returned with status: "PENDING" while screening results are resolved.

Request

POST
/v1/kyc/screen-individual
curl -X POST https://id.zb.co.zw/v1/kyc/screen-individual \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "fullName": "John Moyo",
    "dateOfBirth": "1990-01-15",
    "gender": "MALE",
    "nationality": "ZW",
    "nationalId": "63-123456A78"
  }'

Response

{
  "id": "b2d1a7f0-3c44-4e91-8f2a-1b9c6d5e4a30",
  "userId": null,
  "nationalId": "63-123456A78",
  "type": "WORLDCHECK",
  "status": "PENDING",
  "statusReason": null,
  "externalRef": "3d9c1a8b7e6f5d4c3b2a1908f",
  "cacheHit": false,
  "createdAt": "2026-07-10T09:30:00Z"
}

POST/v1/kyc/screen-corporate

Screen an organisation

Open a World-Check AML screening case for an organisation. Requires the kyc:screen scope. Cases are deduplicated by the normalized organisation name; a matching prior case is reused (cacheHit: true).

Request body

  • Name
    fullName
    Type
    string
    Description

    The organisation's name. Up to 80 characters.

  • Name
    nationality
    Type
    string
    Description

    ISO 3166-1 alpha-2 country code (for example, ZW).

  • Name
    userId
    Type
    string (UUID)
    Description

    Optional. Link the verification to an existing user.

Request

POST
/v1/kyc/screen-corporate
curl -X POST https://id.zb.co.zw/v1/kyc/screen-corporate \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "fullName": "Example Holdings Ltd",
    "nationality": "ZW"
  }'

Response

{
  "id": "f4a2c6b8-9d10-4e23-8a71-2c3d4e5f6a70",
  "userId": null,
  "nationalId": "example holdings ltd",
  "type": "WORLDCHECK",
  "status": "PENDING",
  "statusReason": null,
  "externalRef": "9a8b7c6d5e4f3a2b1c0d9e8f7",
  "cacheHit": false,
  "createdAt": "2026-07-10T09:35:00Z"
}

POST/v1/kyc/screen-result

Retrieve screening results

Fetch the stored screening results for a World-Check case. Requires the kyc:screen scope. Returns the raw World-Check result map keyed by caseSystemId.

Request body

  • Name
    caseSystemId
    Type
    string
    Description

    The World-Check case system id whose results you want to read.

Request

POST
/v1/kyc/screen-result
curl -X POST https://id.zb.co.zw/v1/kyc/screen-result \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "caseSystemId": "3d9c1a8b7e6f5d4c3b2a1908f"
  }'

Response

{
  "caseSystemId": "3d9c1a8b7e6f5d4c3b2a1908f",
  "results": [],
  "resolution": null
}

GET/v1/kyc/screen-toolkit

Fetch the screening toolkit

Fetch the World-Check resolution toolkit: the statuses, risks, and reasons available when resolving a case. Requires the kyc:screen scope. Use the returned ids when recording a resolution decision.

Request

GET
/v1/kyc/screen-toolkit
curl https://id.zb.co.zw/v1/kyc/screen-toolkit \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

Response

{
  "statuses": [
    { "id": "st-positive", "name": "POSITIVE" },
    { "id": "st-possible", "name": "POSSIBLE" },
    { "id": "st-false", "name": "FALSE" },
    { "id": "st-unspecified", "name": "UNSPECIFIED" }
  ],
  "risks": [
    { "id": "rk-high", "name": "HIGH" },
    { "id": "rk-medium", "name": "MEDIUM" },
    { "id": "rk-low", "name": "LOW" },
    { "id": "rk-unknown", "name": "UNKNOWN" }
  ],
  "reasons": [
    { "id": "rs-full", "name": "FULL_MATCH" },
    { "id": "rs-partial", "name": "PARTIAL_MATCH" },
    { "id": "rs-no", "name": "NO_MATCH" },
    { "id": "rs-unknown", "name": "UNKNOWN" }
  ]
}

POST/v1/kyc/screen-resolve

Resolve a screening case

Record a resolution decision on a World-Check case. Requires the kyc:screen scope. Supply the ids from the screening toolkit for the status, risk, and reason. The decision is forwarded to the provider, and its boolean outcome is returned as success.

When you also pass a verificationId, and the provider accepts the resolution, the linked verification record is transitioned:

  • A FALSE status (a cleared false positive) moves the verification to VERIFIED.
  • A POSITIVE status moves it to REJECTED.
  • A POSSIBLE or UNSPECIFIED status leaves it PENDING.

On a successful resolve, the resolutionRemark is stored on the verification and the resolution outcome is merged into its responsePayload. If the supplied verificationId does not match a record, the resolve still returns success from the provider, but no verification is transitioned and none is returned.

Request body

  • Name
    caseSystemId
    Type
    string
    Description

    The World-Check case system id being resolved.

  • Name
    resultIds
    Type
    array
    Description

    One or more result ids from the case being resolved. At least one is required.

  • Name
    statusId
    Type
    string
    Description

    The resolution status id from the toolkit (for example, the id for FALSE or POSITIVE).

  • Name
    riskId
    Type
    string
    Description

    The risk id from the toolkit.

  • Name
    reasonId
    Type
    string
    Description

    The reason id from the toolkit.

  • Name
    resolutionRemark
    Type
    string
    Description

    Optional. A free-text remark stored against the resolution. Truncated to 255 characters when persisted on the verification.

  • Name
    verificationId
    Type
    string (UUID)
    Description

    Optional. The KYC verification record to transition on a successful resolve.

Request

POST
/v1/kyc/screen-resolve
curl -X POST https://id.zb.co.zw/v1/kyc/screen-resolve \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "caseSystemId": "3d9c1a8b7e6f5d4c3b2a1908f",
    "resultIds": ["res-1"],
    "statusId": "st-false",
    "riskId": "rk-low",
    "reasonId": "rs-no",
    "resolutionRemark": "Cleared: not the same person",
    "verificationId": "b2d1a7f0-3c44-4e91-8f2a-1b9c6d5e4a30"
  }'

Response

{
  "success": true,
  "verification": {
    "id": "b2d1a7f0-3c44-4e91-8f2a-1b9c6d5e4a30",
    "userId": null,
    "nationalId": "63-123456A78",
    "type": "WORLDCHECK",
    "status": "VERIFIED",
    "statusReason": "Cleared: not the same person",
    "externalRef": "3d9c1a8b7e6f5d4c3b2a1908f",
    "cacheHit": false,
    "createdAt": "2026-07-10T09:30:00Z"
  }
}

GET/v1/kyc/verifications/{id}

Get a verification by ID

Fetch a single verification record by its id. Requires the kyc:screen scope. Returns the verification object. An unknown id returns an error.

Path parameters

  • Name
    id
    Type
    string (UUID)
    Description

    The id of the verification record to retrieve.

Request

GET
/v1/kyc/verifications/{id}
curl https://id.zb.co.zw/v1/kyc/verifications/7c9e6679-7425-40de-944b-e07fc1f90ae7 \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

Response

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "nationalId": "63-123456A78",
  "type": "EGOV",
  "status": "VERIFIED",
  "statusReason": null,
  "externalRef": null,
  "cacheHit": false,
  "requestSnapshot": { "nationalId": "63-123456A78" },
  "responsePayload": { "Status": "A" },
  "createdAt": "2026-07-10T09:12:00Z"
}

GET/v1/kyc/verifications

List verifications

List the verification records for a national ID, most recent first. Requires the kyc:screen scope. Filter by verification type with the optional type query parameter. Returns an array of verification objects.

The nationalId is matched against the value stored on each record, which for watchlist screenings may be the normalized identity key rather than a literal national ID.

Query parameters

  • Name
    nationalId
    Type
    string
    Description

    Required. The national ID, or World-Check identity key, to look up.

  • Name
    type
    Type
    string
    Description

    Optional. Restrict the results to one type: EGOV, FCB, or WORLDCHECK.

Request

GET
/v1/kyc/verifications
curl "https://id.zb.co.zw/v1/kyc/verifications?nationalId=63-123456A78&type=EGOV" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

Response

[
  {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "nationalId": "63-123456A78",
    "type": "EGOV",
    "status": "VERIFIED",
    "statusReason": null,
    "externalRef": null,
    "cacheHit": false,
    "requestSnapshot": { "nationalId": "63-123456A78" },
    "responsePayload": { "Status": "A" },
    "createdAt": "2026-07-10T09:12:00Z"
  }
]

Was this page helpful?