KYC Verification Guide

Two verification modes, step-by-step flows, status transitions, and bulk operations.

KYC Verification Guide

Every user must be identity-verified before they can make a payment through UrbanPayX . This guide covers both verification modes, step-by-step flows, status transitions, and bulk operations.


Why verification is required

Open banking regulations require that payment recipients verify the identity of payers. UrbanPayX enforces this at the API level — the payment link endpoint will reject any request for a user who has not completed verification.


Two verification modes

UrbanPayX supports two approaches to identity verification. Choose based on whether you want UrbanPayX to handle verification or do it yourself.

UrbanPayX KYCExternal Attestation
Mode valueurbanpayx_kycexternal_attestation
Who verifiesUrbanPayX (via hosted provider)You (your own process)
User experienceUser completes ID + liveness check on hosted pageYou verify identity through your own process, then tell UrbanPayX
Status fieldkyc_statusexternal_verification_status
Payment-eligible statusapprovedverified
Best forWhen you need compliant identity verification without building it yourselfWhen you already have a KYC process and want to bring your own verification results

Your client account has a default verification mode. When you create a user without specifying verification_mode, it inherits the default. You can override the mode per user.


UrbanPayX KYC flow

This mode uses a hosted verification page where users upload identity documents and complete a liveness check.

Step 1 — Create the user

POST /api/v1/kyc/users
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "email": "[email protected]",
  "full_name": "Jane Smith",
  "verification_mode": "urbanpayx_kyc"
}

The user is created with kyc_status: none.

Step 2 — Initiate verification

POST /api/v1/kyc/verify
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "email": "[email protected]",
  "full_name": "Jane Smith",
  "level_name": "id-and-liveness"
}

Verification levels:

LevelWhat it checks
id-and-livenessID document upload + live selfie check
id-onlyID document upload only
idv-and-phone-verificationID document + phone number verification

The response includes a verification_url — this is a hosted page where the user completes the identity check. The link is valid for 7 days.

{
  "user_id": "uuid",
  "kyc_status": "pending",
  "verification_url": "https://verification-provider.com/...",
  "message": "KYC verification initiated"
}

Step 3 — User completes verification

Send the verification_url to your user. They will upload their ID document and (for liveness checks) take a live selfie. This happens entirely on the hosted verification page — no integration work needed on your side.

Step 4 — Receive the result

When the verification is complete, UrbanPayX sends a kyc.status_changed webhook to your configured webhook URL:

{
  "type": "kyc.status_changed",
  "data": {
    "user_id": "uuid",
    "previous_status": "pending",
    "current_status": "approved"
  }
}

You can also poll the status:

GET /api/v1/kyc/status/USER_ID

Status transitions

none --> pending --> approved
                --> rejected
StatusMeaningCan generate payment link?
noneNo verification initiatedNo
pendingVerification in progressNo
approvedIdentity verifiedYes
rejectedVerification failedNo

If a user is rejected: The rejection is final for that verification attempt. You can check the reason and, if appropriate, initiate a new verification attempt.

If a user is already approved: Calling the verify endpoint returns the current approved status immediately without generating a new link.


External attestation flow

This mode lets you use your own identity verification process and report the result to UrbanPayX.

Step 1 — Create the user

POST /api/v1/kyc/users
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "email": "[email protected]",
  "full_name": "Jane Smith",
  "verification_mode": "external_attestation"
}

The user is created with external_verification_status: pending.

Step 2 — Verify the user through your own process

Run your own identity checks (document verification, database lookups, manual review — whatever your process requires).

Step 3 — Report the result

POST /api/v1/kyc/external/status/USER_ID
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "status": "verified",
  "provider": "your-kyc-provider",
  "reference": "your-internal-ref-123",
  "reason": "Identity verified via document check",
  "payload": {
    "document_type": "passport",
    "verified_at": "2026-03-26"
  }
}

The provider, reference, reason, and payload fields are optional but recommended for your audit trail. All status changes are recorded in an audit log with the acting account ID and timestamp.

Status transitions

pending --> verified
        --> rejected
StatusMeaningCan generate payment link?
pendingWaiting for your attestationNo
verifiedYou confirmed identityYes
rejectedYou rejected identityNo

Switching verification modes

You can change a user's verification mode after creation:

PATCH /api/v1/kyc/users/USER_ID/verification-mode
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "verification_mode": "external_attestation"
}

The user retains their existing verification data for both modes. The system checks eligibility based on the active mode.


Bulk operations

For onboarding large numbers of users, UrbanPayX supports bulk endpoints.

Bulk create users

Create up to 500 users in a single request:

POST /api/v1/kyc/users/bulk
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "users": [
    { "email": "[email protected]", "full_name": "User One" },
    { "email": "[email protected]", "full_name": "User Two", "verification_mode": "external_attestation" }
  ]
}

The response reports per-item success or failure, supporting partial success:

{
  "total": 2,
  "succeeded": 2,
  "failed": 0,
  "results": [
    { "index": 0, "email": "[email protected]", "success": true, "user": {} },
    { "index": 1, "email": "[email protected]", "success": true, "user": {} }
  ]
}

Bulk update external verification

Update verification status for up to 500 users at once (external attestation mode only):

POST /api/v1/kyc/external/status/bulk
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "items": [
    {
      "user_id": "uuid-1",
      "status": "verified",
      "provider": "your-provider",
      "reference": "ref-001"
    },
    {
      "user_id": "uuid-2",
      "status": "rejected",
      "provider": "your-provider",
      "reference": "ref-002",
      "reason": "Document expired"
    }
  ]
}

This also supports partial success — individual items can fail (e.g., wrong verification mode) without blocking the rest.


Listing users

Retrieve all users with pagination:

GET /api/v1/kyc/lists?page=1&page_size=50
Authorization: Bearer YOUR_ACCESS_TOKEN

Payment eligibility summary

Before a payment link can be generated, the system checks the user's verification status based on their active mode:

ModeAllowedBlocked (error code)
urbanpayx_kyckyc_status = approvedKYC_REQUIRED (none or rejected), KYC_PENDING (pending)
external_attestationexternal_verification_status = verifiedEXTERNAL_VERIFICATION_PENDING (pending), EXTERNAL_VERIFICATION_REJECTED (rejected)

If you get a 400 error when generating a payment link, check the user's verification status first.


API endpoints reference

MethodPathDescription
POST/kyc/usersCreate a single user
POST/kyc/users/bulkBulk create users (max 500)
PATCH/kyc/users/USER_ID/verification-modeSwitch verification mode
POST/kyc/verifyInitiate UrbanPayX KYC verification
GET/kyc/status/USER_IDGet user verification status
GET/kyc/listsList all users (paginated)
POST/kyc/external/status/USER_IDUpdate external verification status
POST/kyc/external/status/bulkBulk update external status (max 500)

Related guides