KYC Validation

POST/v1/kyc/validations

Verify a customer against RDCPASS, the national digital identity of the Democratic Republic of the Congo, in a single call. Send an identifier — an RDCPASS ID, passport, CENI voter card, driving licence or national ID — and RDCPASS tells you whether a certified identity exists for it, compares any claims you collected, and returns the customer’s basic KYC plus every additional scope your application is granted. Like every RDCPASS endpoint, this call requires the full authentication stack described in Authentication.

At a glance

FactValue
Identifiersrdcpass_id (primary), passport, ceni, driving_licence, national_id
Resultsverified, not_found, not_certified
Data returnedBasic KYC on every verified result, plus the additional scopes granted to your application
Request modesSingle or batch, synchronous or asynchronous
Webhook eventkyc_validation.completed (async single), batch.completed / batch.completed_with_errors (async batch)

Endpoints

MethodPathPurpose
POST/v1/kyc/validationsValidate one identity.
POST/v1/kyc/validations/batchesValidate up to 50 identities synchronously, or up to 10,000 asynchronously.
GET/v1/kyc/validations/{id}Retrieve a previous validation by its id.
GET/v1/jobs/{job_id}Poll an asynchronous single validation.
GET/v1/batches/{batch_id}/resultsPage through the results of an asynchronous batch.

Request modes

KYC Validation supports all four request modes. The request and result shapes are identical in each — only the envelope changes. See Single, batch & async for the full model.

Synchronous (default)Asynchronous (Prefer: respond-async)
Single — POST /v1/kyc/validations200 with the kyc_validation object.202 with a job object; result via the kyc_validation.completed webhook or GET /v1/jobs/{job_id}.
Batch — POST /v1/kyc/validations/batches200 with every item’s result — up to 50 items.202 with a batch object — up to 10,000 items; results via webhook or GET /v1/batches/{batch_id}/results.

Request body

Send the following fields as JSON. As with every RDCPASS request, this is the plaintext shape before AES-256-GCM encryption — see Authentication for how to encrypt and sign it.

FieldTypeRequiredDescription
identifierobjectYesThe identifier to look up.
identifier.typestring enumYesOne of rdcpass_id, passport, ceni, driving_licence, national_id. See Identifier types below.
identifier.valuestringYesThe identifier exactly as printed on the document or shown in the RDCPASS app. Spaces and dashes are normalized.
matchobjectNoClaims you collected from the customer and want compared with the national record. Each claim you send gets its own verdict in the response.
match.full_namestringNoFull name as given by the customer. Compared tolerantly: order of names, accents and letter case do not matter.
match.date_of_birthstringNoDate of birth, YYYY-MM-DD.
match.ageintegerNoAge in whole years — useful when you collected an age rather than a date of birth.
purposestring enumYesWhy you are performing this lookup. Must be one of the purposes approved for your application.
scopesstring[]NoAdditional scopes to return, as a subset of the scopes granted to your application. Omit it to receive every granted scope; send an empty array to receive basic KYC only.
document_deliverystring enumNoHow document images and biometric files are delivered: signed_url (default) or base64. See Documents & biometrics.
referencestringIn batchesYour own identifier for this customer, echoed back in the response. Optional for a single call, required for every batch item.
Request body (plaintext, before encryption)
{
  "reference": "cust-0001",
  "identifier": {
    "type": "rdcpass_id",
    "value": "COD-2103-0214-4937"
  },
  "match": {
    "full_name": "Kabeya Mwamba Tshisekedi",
    "date_of_birth": "1988-04-12",
    "age": 38
  },
  "purpose": "customer_onboarding",
  "document_delivery": "signed_url"
}

Optional headers

HeaderDescription
Idempotency-KeyA UUID you generate. Retrying with the same key within 24 hours returns the original response instead of performing — and billing — a second validation.
PreferSet to respond-async to run the call asynchronously. See Asynchronous validation below.

Identifier types

The RDCPASS ID is the primary identifier and resolves directly to one identity. The other types resolve through the documents registered on the citizen’s RDCPASS record.

identifier.typeDocumentExample
rdcpass_idRDCPASS national digital identity numberCOD-2103-0214-4937
passportPassport issued by the DRCOB1234567
ceniCENI voter card1234567890123
driving_licenceDriving licenceKIN-DL-0457812
national_idNational identity cardCD-NID-0081245573

Purposes

Every call declares a purpose. The purposes your application may use are approved during production review; any other value returns 403 purpose_not_approved. The accepted values are:

  • customer_onboarding
  • account_recovery
  • transaction_authorization
  • age_gating
  • regulatory_compliance
  • fraud_prevention
  • credit_assessment
  • employment_screening

Results

resultMeaningReturns
verifiedA certified RDCPASS account exists for the identifier.account, match, kyc (basic KYC + granted scopes)
not_foundNo certified account exists for the identifier.No account or kyc block.
not_certifiedAn account exists but has not yet been certified or activated.account only — no kyc block.

Treat not_certified as unverified

A not_certified account has been created but has not completed certification, so its identity has not been established by RDCPASS. Do not onboard on it — ask the customer to complete certification in the RDCPASS app and validate again.

Match verdicts

For each claim you send in match, the response returns one of the values below, plus an overall score from 0 to 1. Claims you did not send are reported as not_provided.

ValueMeaning
matchThe claim matches the national record.
partial_matchThe claim is close but not identical — for example a missing middle name or a transposed day and month.
no_matchThe claim does not match the national record.
not_providedYou did not send this claim.

Response body

A successful call returns 200 OK with a kyc_validation object, once decrypted the same way you encrypted the request.

FieldTypeDescription
idstringUnique id of this validation, prefixed kyc_.
objectstringAlways "kyc_validation".
livemodebooleantrue in production, false in the sandbox.
referencestring | nullYour reference, echoed back.
statusstring enumAlways "completed" for a synchronous call. Asynchronous and batch items can be "failed".
resultstring enumverified, not_found or not_certified. See Results.
certified_accountbooleantrue when a certified RDCPASS account exists for the identifier.
identifierobjectThe identifier you sent, normalized.
matchobjectOne verdict per claim you sent, plus an overall score (0–1). Present when result is verified.
accountobjectrdcpass_id, status (active | suspended | paused | deleted), certified, created_at and level_of_assurance (LOA2 | LOA3 | LOA4).
kycobjectbasic is always present on a verified result. Every other block appears only when its scope was applied — see the table below.
scopes_appliedstring[]The scopes whose data is in this response, always including kyc.basic.
scopes_withheldstring[]Scopes you requested and are granted, but for which the citizen has no data on record. They are omitted, not an error.
purposestringThe purpose you declared.
created_atstringRFC 3339 timestamp of the validation.

KYC blocks

The kyc object is assembled from basic KYC plus one block per applied scope:

BlockScopeContents
kyc.basic—full_name, first_name, last_name, date_of_birth, age, gender, nationality — basic KYC, no subscription needed.
kyc.documents.primarykyc.documents.primaryThe primary identity document: type, number, issued_at, expires_at, issuing_authority, status and image (a file object).
kyc.documents.otherskyc.documents.allEvery other document on record, same shape as primary.
kyc.addresseskyc.addressesAddresses: type, province, city, commune, quartier, avenue, number, is_primary, verified_at.
kyc.emailskyc.emailsEmail addresses: address, is_primary, verified.
kyc.phone_numberskyc.phone_numbersPhone numbers in E.164: number, operator, is_primary, verified.
kyc.professionskyc.professionsProfessions and employment: title, employer, sector, since.
kyc.place_of_birthkyc.place_of_birthcountry, province, city.
kyc.marital_statuskyc.marital_statussingle, married, divorced or widowed.
kyc.languageskyc.languagesSpoken languages as ISO 639 codes, e.g. fra, lin, swa, kon, lua.
kyc.religionkyc.religionReligion. Sensitive tier.
kyc.ethnicitykyc.ethnicityEthnic group. Sensitive tier.
kyc.biometrics.selfiekyc.biometrics.selfieEnrollment selfie: captured_at and file. Biometric tier.
kyc.biometrics.fingerprintskyc.biometrics.fingerprintFingerprint templates (ISO/IEC 19794-2), one per finger: format, finger, file. Biometric tier.
kyc.biometrics.iriskyc.biometrics.irisIris templates (ISO/IEC 19794-6): format, eye (left | right), file. Biometric tier.

The response below is for an application granted every scope, with the request above (no scopes field, so every granted scope applies):

200 OK — verified, every scope granted (after decryption)
{
  "id": "kyc_8d2f6a1c93",
  "object": "kyc_validation",
  "livemode": true,
  "reference": "cust-0001",
  "status": "completed",
  "result": "verified",
  "certified_account": true,
  "identifier": {
    "type": "rdcpass_id",
    "value": "COD-2103-0214-4937"
  },
  "match": {
    "full_name": "match",
    "date_of_birth": "match",
    "age": "match",
    "score": 0.98
  },
  "account": {
    "rdcpass_id": "COD-2103-0214-4937",
    "status": "active",
    "certified": true,
    "created_at": "2025-03-14T09:22:41Z",
    "level_of_assurance": "LOA3"
  },
  "kyc": {
    "basic": {
      "full_name": "Kabeya Mwamba Tshisekedi",
      "first_name": "Kabeya",
      "last_name": "Tshisekedi",
      "date_of_birth": "1988-04-12",
      "age": 38,
      "gender": "male",
      "nationality": "COD"
    },
    "documents": {
      "primary": {
        "type": "passport",
        "number": "OB1234567",
        "issued_at": "2022-06-01",
        "expires_at": "2027-05-31",
        "issuing_authority": "Direction Générale de Migration",
        "status": "valid",
        "image": {
          "content_type": "image/jpeg",
          "url": "https://files.rdcpass.cd/d/9f2c41e8b7a3?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
          "expires_at": "2026-09-26T10:20:02Z"
        }
      },
      "others": [
        {
          "type": "ceni",
          "number": "1234567890123",
          "issued_at": "2023-02-18",
          "expires_at": null,
          "issuing_authority": "Commission Électorale Nationale Indépendante",
          "status": "valid",
          "image": {
            "content_type": "image/jpeg",
            "url": "https://files.rdcpass.cd/d/4b7d02c9e1f5?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
            "expires_at": "2026-09-26T10:20:02Z"
          }
        },
        {
          "type": "driving_licence",
          "number": "KIN-DL-0457812",
          "issued_at": "2021-09-07",
          "expires_at": "2026-09-06",
          "issuing_authority": "Ministère des Transports et Voies de Communication",
          "status": "expired",
          "image": {
            "content_type": "image/jpeg",
            "url": "https://files.rdcpass.cd/d/c83e5a1f0d27?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
            "expires_at": "2026-09-26T10:20:02Z"
          }
        }
      ]
    },
    "addresses": [
      {
        "type": "residential",
        "province": "Kinshasa",
        "city": "Kinshasa",
        "commune": "Gombe",
        "quartier": "Batetela",
        "avenue": "Avenue de la Justice",
        "number": "45",
        "is_primary": true,
        "verified_at": "2025-03-14T09:40:12Z"
      },
      {
        "type": "postal",
        "province": "Haut-Katanga",
        "city": "Lubumbashi",
        "commune": "Lubumbashi",
        "quartier": "Makutano",
        "avenue": "Avenue Kasavubu",
        "number": "1287",
        "is_primary": false,
        "verified_at": null
      }
    ],
    "emails": [
      {
        "address": "kabeya.tshisekedi@example.cd",
        "is_primary": true,
        "verified": true
      },
      {
        "address": "k.mwamba@example.com",
        "is_primary": false,
        "verified": false
      }
    ],
    "phone_numbers": [
      {
        "number": "+243812345678",
        "operator": "Vodacom",
        "is_primary": true,
        "verified": true
      },
      {
        "number": "+243991234567",
        "operator": "Airtel",
        "is_primary": false,
        "verified": true
      }
    ],
    "professions": [
      {
        "title": "Ingénieur réseaux",
        "employer": "Congo Réseaux SARL",
        "sector": "telecommunications",
        "since": "2016-02-01"
      }
    ],
    "place_of_birth": {
      "country": "COD",
      "province": "Kasaï-Oriental",
      "city": "Mbuji-Mayi"
    },
    "marital_status": "married",
    "languages": [
      "fra",
      "lin",
      "lua",
      "swa"
    ],
    "religion": "catholic",
    "ethnicity": "Luba",
    "biometrics": {
      "selfie": {
        "captured_at": "2025-03-14T09:31:55Z",
        "file": {
          "content_type": "image/jpeg",
          "url": "https://files.rdcpass.cd/d/7a1e9d3c5b08?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
          "expires_at": "2026-09-26T10:20:02Z"
        }
      },
      "fingerprints": [
        {
          "format": "ISO_19794_2",
          "finger": "right_index",
          "file": {
            "content_type": "application/octet-stream",
            "url": "https://files.rdcpass.cd/d/e2f47b91ac06?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
            "expires_at": "2026-09-26T10:20:02Z"
          }
        },
        {
          "format": "ISO_19794_2",
          "finger": "left_index",
          "file": {
            "content_type": "application/octet-stream",
            "url": "https://files.rdcpass.cd/d/1d8c6e03b4f9?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
            "expires_at": "2026-09-26T10:20:02Z"
          }
        }
      ],
      "iris": [
        {
          "format": "ISO_19794_6",
          "eye": "left",
          "file": {
            "content_type": "application/octet-stream",
            "url": "https://files.rdcpass.cd/d/5c09a2e7f13d?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
            "expires_at": "2026-09-26T10:20:02Z"
          }
        },
        {
          "format": "ISO_19794_6",
          "eye": "right",
          "file": {
            "content_type": "application/octet-stream",
            "url": "https://files.rdcpass.cd/d/b6f13d84e0a2?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
            "expires_at": "2026-09-26T10:20:02Z"
          }
        }
      ]
    }
  },
  "scopes_applied": [
    "kyc.basic",
    "kyc.documents.primary",
    "kyc.documents.all",
    "kyc.addresses",
    "kyc.emails",
    "kyc.phone_numbers",
    "kyc.professions",
    "kyc.place_of_birth",
    "kyc.marital_status",
    "kyc.languages",
    "kyc.religion",
    "kyc.ethnicity",
    "kyc.biometrics.selfie",
    "kyc.biometrics.fingerprint",
    "kyc.biometrics.iris"
  ],
  "scopes_withheld": [],
  "purpose": "customer_onboarding",
  "created_at": "2026-09-26T10:15:02Z"
}

The two other results carry far less data:

200 OK — not_found
{
  "id": "kyc_2a91c7e04d",
  "object": "kyc_validation",
  "livemode": true,
  "reference": "cust-0002",
  "status": "completed",
  "result": "not_found",
  "certified_account": false,
  "identifier": {
    "type": "passport",
    "value": "OB7654321"
  },
  "scopes_applied": [],
  "scopes_withheld": [],
  "purpose": "customer_onboarding",
  "created_at": "2026-09-26T10:15:02Z"
}
200 OK — not_certified
{
  "id": "kyc_5e3b8f21a6",
  "object": "kyc_validation",
  "livemode": true,
  "reference": "cust-0003",
  "status": "completed",
  "result": "not_certified",
  "certified_account": false,
  "identifier": {
    "type": "rdcpass_id",
    "value": "COD-2607-1182-0356"
  },
  "account": {
    "rdcpass_id": "COD-2607-1182-0356",
    "status": "active",
    "certified": false,
    "created_at": "2026-09-20T16:04:37Z",
    "level_of_assurance": "LOA2"
  },
  "scopes_applied": [],
  "scopes_withheld": [],
  "purpose": "customer_onboarding",
  "created_at": "2026-09-26T10:15:02Z"
}

Example request

A single synchronous validation narrowed to two scopes. The header values are illustrative placeholders — see Authentication for exactly how to compute a real X-RDCPASS-Signature and encrypt the body.

validate-identity.sh
# Plaintext body shown — encrypt it per /docs/authentication before sending.
curl https://api.rdcpass.cd/v1/kyc/validations \
  -X POST \
  -H "X-RDCPASS-Key-Id: key_live_8f2a1c0e9b" \
  -H "X-RDCPASS-Timestamp: 1790417700" \
  -H "X-RDCPASS-Nonce: 6f1c9b2e-4a3d-4e11-9c7a-2d8e5f1b0a44" \
  -H "X-RDCPASS-Signature: 3f7a9c...e21d" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "cust-0001",
    "identifier": {
      "type": "rdcpass_id",
      "value": "COD-2103-0214-4937"
    },
    "match": {
      "full_name": "Kabeya Mwamba Tshisekedi",
      "date_of_birth": "1988-04-12"
    },
    "purpose": "customer_onboarding",
    "scopes": [
      "kyc.documents.primary",
      "kyc.phone_numbers"
    ],
    "document_delivery": "signed_url"
  }'
200 OK
{
  "id": "kyc_8d2f6a1c93",
  "object": "kyc_validation",
  "livemode": true,
  "reference": "cust-0001",
  "status": "completed",
  "result": "verified",
  "certified_account": true,
  "identifier": {
    "type": "rdcpass_id",
    "value": "COD-2103-0214-4937"
  },
  "match": {
    "full_name": "match",
    "date_of_birth": "match",
    "age": "not_provided",
    "score": 0.98
  },
  "account": {
    "rdcpass_id": "COD-2103-0214-4937",
    "status": "active",
    "certified": true,
    "created_at": "2025-03-14T09:22:41Z",
    "level_of_assurance": "LOA3"
  },
  "kyc": {
    "basic": {
      "full_name": "Kabeya Mwamba Tshisekedi",
      "first_name": "Kabeya",
      "last_name": "Tshisekedi",
      "date_of_birth": "1988-04-12",
      "age": 38,
      "gender": "male",
      "nationality": "COD"
    },
    "documents": {
      "primary": {
        "type": "passport",
        "number": "OB1234567",
        "issued_at": "2022-06-01",
        "expires_at": "2027-05-31",
        "issuing_authority": "Direction Générale de Migration",
        "status": "valid",
        "image": {
          "content_type": "image/jpeg",
          "url": "https://files.rdcpass.cd/d/9f2c41e8b7a3?sig=Qm9fX2t5Y19zaWc&exp=1790418002",
          "expires_at": "2026-09-26T10:20:02Z"
        }
      }
    },
    "phone_numbers": [
      {
        "number": "+243812345678",
        "operator": "Vodacom",
        "is_primary": true,
        "verified": true
      },
      {
        "number": "+243991234567",
        "operator": "Airtel",
        "is_primary": false,
        "verified": true
      }
    ]
  },
  "scopes_applied": [
    "kyc.basic",
    "kyc.documents.primary",
    "kyc.phone_numbers"
  ],
  "scopes_withheld": [],
  "purpose": "customer_onboarding",
  "created_at": "2026-09-26T10:15:02Z"
}

Basic KYC vs additional scopes

Every verified result includes basic KYC — the account block and kyc.basic — at no extra subscription. Its field set is configured by RDCPASS as platform-wide policy.

Everything else is an additional scope. Your organization subscribes to a scope, your application selects it at creation, and each request can narrow the set further with scopes. Religion and ethnicity are sensitive scopes; selfie, fingerprints and iris are biometric scopes — both tiers require enhanced due diligence. Each additional scope is billed per returned record on top of the base call price; a not_found result is billed at the base rate. Read KYC scopes & subscriptions for the full catalogue, Documents & biometrics for how files are delivered, and Billing for pricing.

Batch validation

Send up to 50 items to POST /v1/kyc/validations/batches and receive every result in the same response. Batch-level purpose, scopes and document_delivery apply to every item unless an item overrides them. Each item needs its own reference, which is echoed back so you can reconcile results. One invalid item never fails the batch — it comes back with "status": "failed" and an error.

Synchronous batch (3 items)
# Plaintext body shown — encrypt it per /docs/authentication before sending.
curl https://api.rdcpass.cd/v1/kyc/validations/batches \
  -X POST \
  -H "X-RDCPASS-Key-Id: key_live_8f2a1c0e9b" \
  -H "X-RDCPASS-Timestamp: 1790417700" \
  -H "X-RDCPASS-Nonce: 6f1c9b2e-4a3d-4e11-9c7a-2d8e5f1b0a44" \
  -H "X-RDCPASS-Signature: 3f7a9c...e21d" \
  -H "Idempotency-Key: 0b6e2f4a-7d1c-4c8e-9a35-1f2d3c4b5a69" \
  -H "Content-Type: application/json" \
  -d '{
    "purpose": "customer_onboarding",
    "scopes": [
      "kyc.phone_numbers"
    ],
    "items": [
      {
        "reference": "cust-0001",
        "identifier": {
          "type": "rdcpass_id",
          "value": "COD-2103-0214-4937"
        },
        "match": {
          "full_name": "Kabeya Mwamba Tshisekedi"
        }
      },
      {
        "reference": "cust-0002",
        "identifier": {
          "type": "ceni",
          "value": "1234567890123"
        },
        "match": {
          "date_of_birth": "1992-11-03"
        }
      },
      {
        "reference": "cust-0003",
        "identifier": {
          "type": "passport",
          "value": "OB12"
        }
      }
    ]
  }'
200 OK — batch results
{
  "object": "list",
  "data": [
    {
      "reference": "cust-0001",
      "status": "succeeded",
      "result": {
        "id": "kyc_8d2f6a1c93",
        "object": "kyc_validation",
        "livemode": true,
        "reference": "cust-0001",
        "status": "completed",
        "result": "verified",
        "certified_account": true,
        "identifier": {
          "type": "rdcpass_id",
          "value": "COD-2103-0214-4937"
        },
        "match": {
          "full_name": "match",
          "date_of_birth": "not_provided",
          "age": "not_provided",
          "score": 0.99
        },
        "account": {
          "rdcpass_id": "COD-2103-0214-4937",
          "status": "active",
          "certified": true,
          "created_at": "2025-03-14T09:22:41Z",
          "level_of_assurance": "LOA3"
        },
        "kyc": {
          "basic": {
            "full_name": "Kabeya Mwamba Tshisekedi",
            "first_name": "Kabeya",
            "last_name": "Tshisekedi",
            "date_of_birth": "1988-04-12",
            "age": 38,
            "gender": "male",
            "nationality": "COD"
          },
          "phone_numbers": [
            {
              "number": "+243812345678",
              "operator": "Vodacom",
              "is_primary": true,
              "verified": true
            }
          ]
        },
        "scopes_applied": [
          "kyc.basic",
          "kyc.phone_numbers"
        ],
        "scopes_withheld": [],
        "purpose": "customer_onboarding",
        "created_at": "2026-09-26T10:15:02Z"
      }
    },
    {
      "reference": "cust-0002",
      "status": "succeeded",
      "result": {
        "id": "kyc_3f7c0b92e5",
        "object": "kyc_validation",
        "livemode": true,
        "reference": "cust-0002",
        "status": "completed",
        "result": "verified",
        "certified_account": true,
        "identifier": {
          "type": "ceni",
          "value": "1234567890123"
        },
        "match": {
          "full_name": "not_provided",
          "date_of_birth": "match",
          "age": "not_provided",
          "score": 1
        },
        "account": {
          "rdcpass_id": "COD-1908-0521-7730",
          "status": "active",
          "certified": true,
          "created_at": "2025-03-14T09:22:41Z",
          "level_of_assurance": "LOA3"
        },
        "kyc": {
          "basic": {
            "full_name": "Mbuyi Ilunga Nsimba",
            "first_name": "Mbuyi",
            "last_name": "Nsimba",
            "date_of_birth": "1992-11-03",
            "age": 33,
            "gender": "female",
            "nationality": "COD"
          },
          "phone_numbers": [
            {
              "number": "+243851122334",
              "operator": "Orange",
              "is_primary": true,
              "verified": true
            }
          ]
        },
        "scopes_applied": [
          "kyc.basic",
          "kyc.phone_numbers"
        ],
        "scopes_withheld": [],
        "purpose": "customer_onboarding",
        "created_at": "2026-09-26T10:15:02Z"
      }
    },
    {
      "reference": "cust-0003",
      "status": "failed",
      "error": {
        "code": "invalid_identifier",
        "message": "identifier.value \"OB12\" is not a valid passport number."
      }
    }
  ],
  "next_cursor": null
}

More than 50 items in a synchronous batch returns 413 batch_too_large. Send it asynchronously instead.

Asynchronous validation

Add Prefer: respond-async to any call to receive 202 Accepted immediately with a job object. When the validation finishes, RDCPASS sends a kyc_validation.completed webhook containing the same kyc_validation object the synchronous call returns. You can also poll GET /v1/jobs/{job_id}.

Asynchronous single validation
# Plaintext body shown — encrypt it per /docs/authentication before sending.
curl https://api.rdcpass.cd/v1/kyc/validations \
  -X POST \
  -H "X-RDCPASS-Key-Id: key_live_8f2a1c0e9b" \
  -H "X-RDCPASS-Timestamp: 1790417700" \
  -H "X-RDCPASS-Nonce: 6f1c9b2e-4a3d-4e11-9c7a-2d8e5f1b0a44" \
  -H "X-RDCPASS-Signature: 3f7a9c...e21d" \
  -H "Idempotency-Key: 0b6e2f4a-7d1c-4c8e-9a35-1f2d3c4b5a69" \
  -H "Prefer: respond-async" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "cust-0001",
    "identifier": { "type": "rdcpass_id", "value": "COD-2103-0214-4937" },
    "purpose": "customer_onboarding",
    "scopes": ["kyc.addresses"]
  }'
202 Accepted — job
{
  "id": "job_4b1d9e7a2c",
  "object": "job",
  "service": "kyc_validation",
  "status": "pending",
  "created_at": "2026-09-26T10:15:00Z",
  "result_url": "/v1/jobs/job_4b1d9e7a2c"
}
Webhook — kyc_validation.completed
{
  "id": "evt_2c8e41f7a9d05b36",
  "object": "event",
  "type": "kyc_validation.completed",
  "livemode": true,
  "created_at": "2026-09-26T10:15:02Z",
  "data": {
    "object": {
      "id": "job_4b1d9e7a2c",
      "object": "job",
      "service": "kyc_validation",
      "status": "completed",
      "created_at": "2026-09-26T10:15:00Z",
      "result_url": "/v1/jobs/job_4b1d9e7a2c",
      "result": {
        "id": "kyc_8d2f6a1c93",
        "object": "kyc_validation",
        "livemode": true,
        "reference": "cust-0001",
        "status": "completed",
        "result": "verified",
        "certified_account": true,
        "identifier": {
          "type": "rdcpass_id",
          "value": "COD-2103-0214-4937"
        },
        "account": {
          "rdcpass_id": "COD-2103-0214-4937",
          "status": "active",
          "certified": true,
          "created_at": "2025-03-14T09:22:41Z",
          "level_of_assurance": "LOA3"
        },
        "kyc": {
          "basic": {
            "full_name": "Kabeya Mwamba Tshisekedi",
            "first_name": "Kabeya",
            "last_name": "Tshisekedi",
            "date_of_birth": "1988-04-12",
            "age": 38,
            "gender": "male",
            "nationality": "COD"
          },
          "addresses": [
            {
              "type": "residential",
              "province": "Kinshasa",
              "city": "Kinshasa",
              "commune": "Gombe",
              "quartier": "Batetela",
              "avenue": "Avenue de la Justice",
              "number": "45",
              "is_primary": true,
              "verified_at": "2025-03-14T09:40:12Z"
            }
          ]
        },
        "scopes_applied": [
          "kyc.basic",
          "kyc.addresses"
        ],
        "scopes_withheld": [],
        "purpose": "customer_onboarding",
        "created_at": "2026-09-26T10:15:02Z"
      }
    }
  }
}

Asynchronous batch

For up to 10,000 items, combine the batch endpoint with Prefer: respond-async. You receive a batch object straight away; follow its progress with GET /v1/batches/{batch_id}, and when it finishes RDCPASS sends batch.completed or batch.completed_with_errors. Results are paged, 500 per page at most — pass next_cursor back as cursor until it is null. Batches larger than 100 items must use signed_url delivery.

Asynchronous batch, then results
# 2,500 items — above the 50-item synchronous limit, so run it asynchronously.
curl https://api.rdcpass.cd/v1/kyc/validations/batches \
  -X POST \
  -H "X-RDCPASS-Key-Id: key_live_8f2a1c0e9b" \
  -H "X-RDCPASS-Timestamp: 1790417700" \
  -H "X-RDCPASS-Nonce: 6f1c9b2e-4a3d-4e11-9c7a-2d8e5f1b0a44" \
  -H "X-RDCPASS-Signature: 3f7a9c...e21d" \
  -H "Idempotency-Key: 0b6e2f4a-7d1c-4c8e-9a35-1f2d3c4b5a69" \
  -H "Prefer: respond-async" \
  -H "Content-Type: application/json" \
  --data-binary @kyc-refresh-2026-09.json

# Later: page through the results (max 500 per page).
curl "https://api.rdcpass.cd/v1/batches/bat_7c2e91f04a/results?limit=500" \
  -H "X-RDCPASS-Key-Id: key_live_8f2a1c0e9b" \
  -H "X-RDCPASS-Timestamp: 1790417700" \
  -H "X-RDCPASS-Nonce: 6f1c9b2e-4a3d-4e11-9c7a-2d8e5f1b0a44" \
  -H "X-RDCPASS-Signature: 3f7a9c...e21d"
202 Accepted — batch
{
  "id": "bat_7c2e91f04a",
  "object": "batch",
  "service": "kyc_validation",
  "status": "pending",
  "total_items": 2500,
  "processed_items": 0,
  "succeeded_items": 0,
  "failed_items": 0,
  "created_at": "2026-09-26T10:15:00Z",
  "completed_at": null,
  "results_url": "/v1/batches/bat_7c2e91f04a/results"
}
Webhook — batch.completed_with_errors
{
  "id": "evt_9a4f7c21e8b36d05",
  "object": "event",
  "type": "batch.completed_with_errors",
  "livemode": true,
  "created_at": "2026-09-26T10:21:47Z",
  "data": {
    "object": {
      "id": "bat_7c2e91f04a",
      "object": "batch",
      "service": "kyc_validation",
      "status": "completed_with_errors",
      "total_items": 2500,
      "processed_items": 2500,
      "succeeded_items": 2486,
      "failed_items": 14,
      "created_at": "2026-09-26T10:15:00Z",
      "completed_at": "2026-09-26T10:21:46Z",
      "results_url": "/v1/batches/bat_7c2e91f04a/results"
    }
  }
}
200 OK — GET /v1/batches/bat_7c2e91f04a/results
{
  "object": "list",
  "data": [
    {
      "reference": "cust-0001",
      "status": "succeeded",
      "result": {
        "id": "kyc_8d2f6a1c93",
        "object": "kyc_validation",
        "livemode": true,
        "reference": "cust-0001",
        "status": "completed",
        "result": "verified",
        "certified_account": true,
        "identifier": {
          "type": "rdcpass_id",
          "value": "COD-2103-0214-4937"
        },
        "match": {
          "full_name": "match",
          "date_of_birth": "not_provided",
          "age": "not_provided",
          "score": 0.99
        },
        "account": {
          "rdcpass_id": "COD-2103-0214-4937",
          "status": "active",
          "certified": true,
          "created_at": "2025-03-14T09:22:41Z",
          "level_of_assurance": "LOA3"
        },
        "kyc": {
          "basic": {
            "full_name": "Kabeya Mwamba Tshisekedi",
            "first_name": "Kabeya",
            "last_name": "Tshisekedi",
            "date_of_birth": "1988-04-12",
            "age": 38,
            "gender": "male",
            "nationality": "COD"
          },
          "phone_numbers": [
            {
              "number": "+243812345678",
              "operator": "Vodacom",
              "is_primary": true,
              "verified": true
            }
          ]
        },
        "scopes_applied": [
          "kyc.basic",
          "kyc.phone_numbers"
        ],
        "scopes_withheld": [],
        "purpose": "customer_onboarding",
        "created_at": "2026-09-26T10:15:02Z"
      }
    },
    {
      "reference": "cust-0002",
      "status": "succeeded",
      "result": {
        "id": "kyc_3f7c0b92e5",
        "object": "kyc_validation",
        "livemode": true,
        "reference": "cust-0002",
        "status": "completed",
        "result": "verified",
        "certified_account": true,
        "identifier": {
          "type": "ceni",
          "value": "1234567890123"
        },
        "match": {
          "full_name": "not_provided",
          "date_of_birth": "match",
          "age": "not_provided",
          "score": 1
        },
        "account": {
          "rdcpass_id": "COD-1908-0521-7730",
          "status": "active",
          "certified": true,
          "created_at": "2025-03-14T09:22:41Z",
          "level_of_assurance": "LOA3"
        },
        "kyc": {
          "basic": {
            "full_name": "Mbuyi Ilunga Nsimba",
            "first_name": "Mbuyi",
            "last_name": "Nsimba",
            "date_of_birth": "1992-11-03",
            "age": 33,
            "gender": "female",
            "nationality": "COD"
          },
          "phone_numbers": [
            {
              "number": "+243851122334",
              "operator": "Orange",
              "is_primary": true,
              "verified": true
            }
          ]
        },
        "scopes_applied": [
          "kyc.basic",
          "kyc.phone_numbers"
        ],
        "scopes_withheld": [],
        "purpose": "customer_onboarding",
        "created_at": "2026-09-26T10:15:02Z"
      }
    },
    {
      "reference": "cust-0003",
      "status": "failed",
      "error": {
        "code": "invalid_identifier",
        "message": "identifier.value \"OB12\" is not a valid passport number."
      }
    }
  ],
  "next_cursor": "cur_Y3VzdC0wMDAz"
}

Errors

Errors use the standard RDCPASS error format described in Errors. A not_found result is not an error — it returns 200 OK. The errors specific to this service are:

CodeHTTPDescription
invalid_identifier400identifier.value is malformed for its type, e.g. a passport number with the wrong length.
unsupported_identifier_type400identifier.type is not one of the accepted values.
insufficient_balance402Prepaid wallet is empty. Top up to resume production calls.
credit_limit_reached402Postpaid usage has reached your credit limit.
scope_not_granted403You requested a scope that is not granted to this application.
purpose_not_approved403The purpose is not approved for this application.
service_not_enabled403KYC Validation is not enabled on this application.
production_access_required403The key is a production key but the application has not been approved for production.
job_not_found404No job with this id exists for your application.
batch_not_found404No batch with this id exists for your application.
batch_too_large413More than 50 items in a synchronous batch, or more than 10,000 in an asynchronous one.
quota_exceeded429Your application has used its quota for the period.
rate_limited429Too many requests per second. Retry with backoff.

Every lookup is attributable and audited

RDCPASS records which application validated which identity, for which purpose, and which scopes were returned. Citizens can see who looked them up in their RDCPASS app. Only validate customers you actually have a relationship with, for the purpose you declare.

Next steps

Questions about your integration? Contact developer support