Universal KYC

Deliver a complete and end-to-end KYC workflow through a single unified API which seamlessly verifying cardholder identity and enabling instant approval for card issuance.

Overview

This flow enables completion of the end-to-end entity onboarding and card issuance process as follows:

  1. Create the entity profile in the system using a standardised JSON structure capturing core user details and identifiers.
  2. Submit the completed KYC payload including ID and proof of address documents for verification through the configured KYC provider.
  3. Receive the approval decision and retrieve the signed KYC payload for approved applications.
  4. The signed payload is used to initiate card creation through the card issuance flow.

Prerequisites

  1. Compliance approval from Reap for the use of Universal KYC for the program.
💬

For questions regarding the Universal KYC approval process, please contact your account manager for assistance.

  1. An established KYC provider is already in place.
  2. Agreement that Reap retains the final approval authority for card issuance under its non-reliance model.

⚠️

This guide contains two post-approval paths:

  • Non-BIN sponsorship clients: must complete Step 6 (Fetch the Signed Payload) and Step 7 (Create Card).
  • BIN sponsorship clients (for example, Bybit): do not need to complete Steps 6 and 7. They may use the verified KYC information directly in their own downstream card issuance flow after KYC is approved.

Step 1: Register and Create Entity

Reap requires an internal record (an Entity) to attach KYC requirements, documents, and decisions. The externalId links the cardholder record to Reap.


Call the Create Entity Endpoint

Use POST /entity to create and register an entity for the cardholder in Reap’s system for KYC processing.


Sample Request (cURL):

curl --request POST \
	--url https://sandbox-compliance.api.reap.global/entity \
	--header 'Accept: application/json' \
	--header 'Content-Type: application/json' \
	--header 'x-reap-api-key: ********' \
	--data '
{
  "externalId": "CUSTOMER-DEMO-10001",
  "type": "INDIVIDUAL"
}
'

Key Input:

  1. externalId: Unique ID to identify the cardholder (defined and managed in your system)
  2. type: Type of entity being created (Must be set to INDIVIDUAL)

Sample Response:

{
  "id": "5f54bcf9-5a21-4f76-a819-6a65a8c238b8",
  "externalId": "CUSTOMER-TEST-0000307",
  "businessId": "e10e9f95-f256-4c47-bcf6-a6029e09c3aa",
  "type": "INDIVIDUAL"
}

Key Output:

  1. id: Unique identifier generated by Reap for the created entity (Used as entityId in subsequent API calls)
  2. externalId: The external reference ID provided during entity creation
  3. businessId: Unique identifier for the associated business account
  4. type: Type of entity created

Step 2: Submit KYC Details

Universal KYC requires KYC details to be submitted in a standardised schema so requirements can be evaluated consistently across different providers.


Canonical KYC Payload Structure

The Canonical KYC payload must follow the Universal KYC schema and adhere to the required structure shown in the readable object format below:

{
  "externalUserId": "demo2-universal-kyc-ingestinon-entity-1x2",
  "provider":
  {
    "name": "sumsub",
    "status": "APPROVED",
    "data":
    [
      {
        "applicantId": "7c9e4a1f2b6d8e3a5c0f9b12",
        "levelName": "poa_idv_level_v1",
        "verificationStatus": "reviewed",
        "verifiedAt": "2025-01-15T10:22:11Z",
        "webhook":
        {
          "eventType": "applicantReviewed",
          "receivedAt": "2025-01-15T10:22:14Z",
          "payload":
          {
            "reviewStatus": "completed",
            "reviewResult":
            {
              "reviewAnswer": "GREEN"
            }
          }
        },
        "metadata":
        {
          "sumsubCorrelationId": "9f3c2a7b-1d4e-4c8f-a2b6-5e7d9c1f0a3b"
        }
      }
    ]
  },
  "identity":
  {
    "firstName": "Jack",
    "middleName": "Sam",
    "lastName": "Ong",
    "fullName": "Jack Sam Ong",
    "dob": "1994-06-18",
    "nationality": "CAN"
  },
  "document":
  [
    {
      "type": "PASSPORT",
      "number": "K1234567",
      "country": "CAN",
      "issuingDate": "2019-03-10",
      "expiryDate": "2029-03-09"
    }
  ],
  "address":
  {
    "country": "CAN",
    "postCode": "M5V 3L9",
    "town": "Toronto",
    "street": "King Street West",
    "subStreet": "Downtown",
    "state": "ON",
    "buildingName": "TIFF Bell Lightbox",
    "flatNumber": "1208",
    "buildingNumber": "350",
    "formattedAddress": "Unit 1208, 350 King Street West, Toronto, ON M5V 3L9, Canada"
  },
  "liveness":
  {
    "result": "APPROVED",
    "checkedAt": "2025-01-15T10:19:40Z"
  }
}

⚠️

cardProcessorPublicToken is only required when the client is under BIN sponsorship.

The following table defines the canonical Universal KYC payload structure. All fields must adhere to the specified data types, formats, and validation rules. Nested fields are represented using dot notation, and arrays are indicated using [] in Table 1. below:

Variable NameDetails
externalUserIdRequired. String. External user identifier from client system.
cardProcessorPublicTokenOptional. String (max 500 chars). Card processor token.
providerRequired. Object.
provider.nameRequired. String (1–50). Example: sumsub, jumio.
provider.statusRequired. Enum: APPROVED, REJECTED, WARNING, NOT_EXECUTED.
provider.dataRequired. Array<object>. Provider-specific payload (any shape allowed).
provider.data[]Required. Object. Free-form provider data. No fixed schema enforced.
identityRequired. Object.
identity.firstNameRequired. String (1–100).
identity.middleNameOptional. String (≤100).
identity.lastNameOptional. String (≤100).
identity.fullNameRequired. String (1–200). Full legal name per document/provider.
identity.dobRequired. String. Format: YYYY-MM-DD.
identity.nationalityRequired. String. ISO 3166-1 alpha-3 (auto uppercased).
documentRequired. Array<object>. Minimum 1 document required.
document[]Required. Object.
document[].typeRequired. Enum: PASSPORT, ID_CARD, DRIVERS_LICENCE, RESIDENCE_PERMIT.
document[].numberRequired. String (1–50).
document[].countryRequired. String. ISO 3166-1 alpha-3 (issuing country).
document[].issuingDateRequired. String. Format: YYYY-MM-DD.
document[].expiryDateOptional. String. Format: YYYY-MM-DD.
addressRequired. Object.
address.countryRequired. String. ISO 3166-1 alpha-3.
address.postCodeRequired. String (1–20).
address.townOptional. String (≤100).
address.streetRequired. String (1–200).
address.subStreetOptional. String (≤200).
address.stateOptional. String (≤100).
address.buildingNameOptional. String (≤100).
address.flatNumberOptional. String (≤20).
address.buildingNumberOptional. String (≤20).
address.formattedAddressOptional. String (≤500).
livenessRequired. Object.
liveness.resultRequired. Enum: APPROVED, REJECTED, WARNING, NOT_EXECUTED.
liveness.checkedAtConditionally Required. ISO-8601 datetime (e.g. 2026-02-24T10:15:30Z). Required unless result = NOT_EXECUTED.

Table 1. Universal KYC Schema – Canonical Payload Structure


Call the Submit Requirement Endpoint

Use POST /entity/entityId/requirement and pass the canonical KYC payload in the value field as a string to attach the standardised KYC result to the entity.


Sample Request (cURL):

curl --request POST \
  --url https://sandbox-compliance.api.reap.global/entity/5f54bcf9-5a21-4f76-a819-6a65a8c238b8/requirement \
  --header 'Accept: application/json' \
  --header 'content-type: application/json' \
  --header 'x-reap-api-key: ********' \
  --data '{
    "requirementSlug": "ukyc-canonical-kyc-data",
    "value": "{\"externalUserId\":\"demo2-universal-kyc-ingestinon-entity-1x2\",\"provider\":{\"name\":\"sumsub\",\"status\":\"APPROVED\",\"data\":[{\"applicantId\":\"7c9e4a1f2b6d8e3a5c0f9b12\",\"levelName\":\"poa_idv_level_v1\",\"verificationStatus\":\"reviewed\",\"verifiedAt\":\"2025-01-15T10:22:11Z\",\"webhook\":{\"eventType\":\"applicantReviewed\",\"receivedAt\":\"2025-01-15T10:22:14Z\",\"payload\":{\"reviewStatus\":\"completed\",\"reviewResult\":{\"reviewAnswer\":\"GREEN\"}}},\"metadata\":{\"sumsubCorrelationId\":\"9f3c2a7b-1d4e-4c8f-a2b6-5e7d9c1f0a3b\"}}]}},\"identity\":{\"firstName\":\"Jack\",\"middleName\":\"Sam\",\"lastName\":\"Ong\",\"fullName\":\"Jack Sam Ong\",\"dob\":\"1994-06-18\",\"nationality\":\"CAN\"},\"document\":[{\"type\":\"PASSPORT\",\"number\":\"K1234567\",\"country\":\"CAN\",\"issuingDate\":\"2019-03-10\",\"expiryDate\":\"2029-03-09\"}],\"address\":{\"country\":\"CAN\",\"postCode\":\"M5V 3L9\",\"town\":\"Toronto\",\"street\":\"King Street West\",\"subStreet\":\"Downtown\",\"state\":\"ON\",\"buildingName\":\"TIFF Bell Lightbox\",\"flatNumber\":\"1208\",\"buildingNumber\":\"350\",\"formattedAddress\":\"Unit 1208, 350 King Street West, Toronto, ON M5V 3L9, Canada\"},\"liveness\":{\"result\":\"APPROVED\",\"checkedAt\":\"2025-01-15T10:19:40Z\"}}"
  }'

Key Input:

  1. entityId: Unique ID of the entity created in Step 1.
  2. requirementSlug: Identifier of the requirement being fulfilled. For Universal KYC canonical submission, use the following fixed key: ukyc-canonical-kyc-data.
  3. value: Canonical KYC payload submitted in standardised schema formating as a string.
⚠️

The value field must contain the canonical KYC payload serialised as a properly escaped JSON string that strictly follows the Universal KYC schema


Step 3: Submit Identity Document

Uploading the identity document ensures that verifiable evidence of the check is retained, supporting audit requirements and downstream compliance reviews tied to the Entity.


Call the Identity Document Upload Endpoint

Use POST /entity/entityId/requirement to upload the file to the ukyc-id-document requirement slug and file path.


Sample Request (cURL):

curl --request POST \
  --url https://sandbox-compliance.api.reap.global/entity/5f54bcf9-5a21-4f76-a819-6a65a8c238b8/requirement-slug/ukyc-id-document/upload \
  --header 'Accept: application/json' \
  --header 'Content-Type: multipart/form-data' \
  --header 'x-reap-api-key: ********' \
  --form 'files=@"passport.pdf"'

Key Input:

  1. entityId: Unique ID of the entity created in Step 1
  2. files: Identity document file submitted as multipart/form-data
⚠️

files upload requirements: Maximum file size: 10 MB Supported file formats: JPG, JPEG, PNG, PDF


Step 4: Submit Proof of Address Document

Proof of address is commonly required for KYC/KYB completeness. Submitting it via the dedicated requirement slug ensures the evidence is correctly classified and evaluated.


Call the Proof of Address Upload Endpoint

Use POST /entity/entityId/requirement to upload the file to the kyc-proof-of-address-document requirement slug, and pass a file path.


Sample Request (cURL):

curl --request POST \
  --url https://sandbox-compliance.api.reap.global/entity/5f54bcf9-5a21-4f76-a819-6a65a8c238b8/requirement-slug/ukyc-proof-of-address-document/upload \
  --header 'Accept: application/json' \
  --header 'Content-Type: multipart/form-data' \
  --header 'x-reap-api-key: ********' \
  --form 'files=@"utilitybill.pdf"'

Key Input:

  1. entityId: Unique ID of the entity created in Step 1
  2. files: Proof of address document submitted as multipart/form-data
⚠️

files upload requirements: Maximum file size: 10 MB

Supported file formats: JPG, JPEG, PNG, PDF


Step 5: KYC Decision

Once all required information and supporting documents have been successfully submitted, the application will enter the final KYC review stage.

At this stage, a final decision state must be attained before proceeding further in the card issuance flow.

⚠️

Only applications in an Approved state can proceed to Step 6 (Signed Payload Retrieval)


Sample Decision Webhook

The KYC decision is communicated via webhook notification.

{
  "eventName": "card_issuance_api_access_updated",
  "eventType": "account_status_change",
  "data": {
    "eventId": "938c65f7-2aad-494d-b488-5256c4cfc370",
    "message": "User profile approved - card generation enabled",
    "entityId": "6ac28264-c21b-4757-b141-1efce00adce6",
    "timestamp": "2026-01-27T13:27:51.027Z",
    "status": "APPROVED"
  }
}
{
  "eventName": "card_issuance_api_access_updated",
  "eventType": "account_status_change",
  "data": {
    "eventId": "fc7b8d1d-9e08-4c61-bef2-3ce4e6162dcf",
    "message": "Universal KYC requirement rejected",
    "entityId": "6c539925-f00c-4b11-bbc8-7edec5bb69b1",
    "status": "REJECTED",
    "moderationComment": "Country of residence (IND) is prohibited",
    "rejectLabels": [
      "PROHIBITED_RESIDENCE"
    ],
    "reviewRejectType": "FINAL",
    "timestamp": "2026-01-27T13:29:58.336Z"
  }
}

Webhook DesisionNotes
ApprovedCardholder has passed KYC checks and can proceed to create a card
RejectedCardholder has not passed KYC checks and not allowed to create a card. You may resubmit requirements and documents to try again later.

Step 6: Fetch the Signed Payload

The signed payload is a portable and tamper-proof representation of an approved KYC application. It serves as the authorisation evidence for proceeding with card issuance.


Call the Signed Payload Endpoint

Use GET /entity/entityId/signed-payload to retrieve the signed payload associated with the approved KYC application. Ensure the correct featureSlug is provided to specify the intended use case.


Sample Request (cURL):

curl --request GET \
  --url https://sandbox-compliance.api.reap.global/entity/5f54bcf9-5a21-4f76-a819-6a65a8c238b8/signed-payload?featureSlug=universal-kyc-card-issuance-kyc-api \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'featureSlug: universal-kyc-card-issuance-kyc-api' \
  --header 'x-reap-api-key: ********'

Key Input:

  1. entityId: Unique ID of the entity created in Step 1
  2. featureSlug: Feature identifier as a fixed header value: universal-kyc-card-issuance-kyc-api

Sample Response:

{
  "firstName": "Jack",
  "lastName": "Sam Ong",
  "dob": "1994-06-18",
  "idDocumentType": "Passport",
  "idDocumentNumber": "K1234567",
  "residentialAddress": 
	{
    "line1": "Flat 1208, 350, TIFF Bell Lightbox, King Street West",
    "line2": "Downtown",
    "city": "Toronto",
    "country": "CAN",
    "postalCode": "M5V 3L9"
  },
  "expiresAt": "2026-03-10T09:18:51.998Z",
  "signature": "bd6565bfb621f6321095e7dc574ea52b3829e29a732440170794fabfc8dec..."
}

Key Output:

  1. firstName: Verified first name from the approved KYC record.
  2. lastName: Verified last name from the approved KYC record.
  3. dob: Date of birth in YYYY-MM-DD format.
  4. residentialAddress: Verified residential address (line1, line2, city, country, postalCode).
  5. idDocumentType: Type of government-issued ID used for verification.
  6. idDocumentNumber: Verified ID document number.
  7. expiresAt: Expiry timestamp of the signed payload (ISO 8601).
  8. signature: Cryptographic signature ensuring payload integrity and authenticity.

Step 7: Create Card

The output from Step 6 must be passed into the Create Card endpoint when creating a card.


Call the Create Card Endpoint

Use POST /cards to create the card and include the signed payload in the request body.


Sample Request (cURL):

curl --request POST \
  --url https://sandbox-compliance.api.reap.global/cards \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Accept-Version: v2.0' \
  --header 'x-reap-api-key: ********' \
  --data '{
    "...": "...",
    "kyc": {
      "firstName": "Jack",
      "lastName": "Sam Ong",
      "dob": "1994-06-18",
      "idDocumentType": "Passport",
      "idDocumentNumber": "K1234567",
      "residentialAddress": {
        "line1": "Flat 1208, 350, TIFF Bell Lightbox, King Street West",
        "line2": "Downtown",
        "city": "Toronto",
        "country": "CAN",
        "postalCode": "M5V 3L9"
      },
      "expiresAt": "2026-03-10T09:18:51.998Z",
      "signature": "bd6565bfb621f6321095e7dc574ea52b3829e29a732440170794fabfc8dec..."
    },
    "...": "..."
  }'

Key Input:

  1. firstName: Verified first name from the approved KYC record.
  2. lastName: Verified last name from the approved KYC record.
  3. dob: Date of birth in YYYY-MM-DD format.
  4. residentialAddress: Verified residential address (line1, line2, city, country, postalCode).
  5. idDocumentType: Type of government-issued ID used for verification.
  6. idDocumentNumber: Verified ID document number.
  7. expiresAt: Expiry timestamp of the signed payload (ISO 8601 format).
  8. signature: Signed JWT returned from the signed-payload endpoint, ensuring payload integrity and KYC approval.

Sample Response:

{
 "id": "fa1bcdae-c101-4986-b385-8083fb43a7ab"
}

Key Output:

  1. id : Unique identifier of the successfully created card.

Refer to the official Create Card documentation for the full request schema and parameters:

👉 https://reap.readme.io/docs/create-cards


Demo

For a fully interactive demo, see below: