Skip to main content

Identity Verification (IDV) API

The Identity Verification API provides a single endpoint for submitting KYC (Know Your Customer) identity verification data. The API runs sanctions screening first, then verifies users through document verification and selfie matching.

Integration Options

There are two ways to integrate identity verification:

  1. Call the API directly — submit verification data to POST /v2/idv/verify yourself using your merchant credentials, as described on this page.
  2. Generate a Web SDK Link — create a hosted, shareable verification link from the portal (or via API) and send it to your end user. No client-side integration required. See Web SDK Link.

Both options require a flow, which you create in the portal and which determines which fields, documents, and verification methods are required. A group containing that flow is optional when calling /v2/idv/verify directly (groupingId), but required when generating a Web SDK Link (groupId). See Flows & Groups before you start.

Base URL

https://api2.cycurid.com

Authentication

/v2/idv/verify accepts either of the following:

  • Basic Authentication — your merchant credentials in the Authorization header:
    • Username: {{merchantApiKey}}
    • Password: {{merchantSecret}}
  • Bearer Token — a short-lived SDK access token issued by the Web SDK Link token exchange (POST /v2/sdk/session/exchange). Used when verification is performed client-side after a user opens a Web SDK Link.

Processing Flow

  1. Validation — field formats, empty values, required fields (configured per flow)
  2. Sanctions Screening — checks applicant against the sanctions database
    • If unreachable → request blocked (fail closed)
    • If no matches → proceed to KYC
    • If matches found → match is recorded and passed along with the KYC result (see Response Format)
  3. KYC Verification — document verification, face match, MRZ extraction, OCR comparison
  4. Logging — request, response, and uploaded files stored in database

Endpoint

POST /v2/idv/verify

Submit identity verification data with user information and identity documents.

Content-Type

multipart/form-data

Empty Fields

Any field present in the request must have a non-empty value. Sending a field with an empty string will return a 400 error.

Request Parameters

FieldTypeRequiredDescription
firstNamestringYesUser's first name (English characters only)
lastNamestringYesUser's last name (English characters only)
externalUserIdstringYesUnique identifier for the user in your system
flowIdstringYesID of the flow to run this verification against (max 36 characters). Created in the portal — see Flows & Groups.
groupingIdstringNoID of a group this verification belongs to (max 255 characters). If provided, must be a group that flowId belongs to, or the request is rejected. If omitted, the verification isn't associated with any group.
sessionIdstringNoLinks this call to a session created via the Web SDK Link flow (max 255 characters).
middleNamestringNoUser's middle name (English characters only)
dobstringNoDate of birth in MM/DD/YYYY format
phonestringNoPhone number in international format (e.g., +16045551234)
emailstringNoEmail address
streetstringNoStreet address line 1 (English characters only)
street2stringNoStreet address line 2 (English characters only)
citystringNoCity (English characters only)
statestringNoState or province code (e.g., BC, ON) (English characters only)
postcodestringNoPostal or ZIP code
countrystringNoCountry code (ISO 3166-1 alpha-3, e.g., CAN, USA)
nationalitystringNoNationality code (ISO 3166-1 alpha-3)
documentTypesstringNoComma-separated list: PASSPORT, DRIVERS, ID_CARD
documentCountrystringConditionalCountry code for issued documents (ISO 3166-1 alpha-3). Required when documentTypes is provided.
ssnstringConditionalSocial Security Number. Required when the flow's configuration marks SSN as required (globally or for a specific country override).
Fields Are Configured Per Flow

Which optional fields are required, which document types are accepted, and which biometric method is used are all configured on the flow (flowId) in the portal's Configuration page, including per-country overrides. See Flows & Groups. When a required field is missing, the API returns a specific error listing which fields are missing.

File Upload Fields

All files: max 10 MB, formats JPEG, PNG, WEBP, BMP, PDF.

FieldTypeRequired WhenDescription
passportFrontfiledocumentTypes includes PASSPORTFront side of passport
driversFrontfiledocumentTypes includes DRIVERSFront side of driver's license
driversBackfiledocumentTypes includes DRIVERSBack side of driver's license
idCardFrontfiledocumentTypes includes ID_CARDFront side of ID card
idCardBackfiledocumentTypes includes ID_CARDBack side of ID card
selfiefileFlow's biometric type is selfie file uploadStatic user selfie for face matching
Document Requirements
  • Passport: Front side only — passports never require a back side
  • Driver's License / ID Card: Front + back required as configured on the flow (see country overrides)
  • Biometric field: selfie is required whenever any document is provided, and cannot be submitted alone
  • Empty files: File fields must contain actual file data — empty file fields are rejected

Usage Examples

Example 1: Passport with OCR Match

curl -X POST 'https://api2.cycurid.com/v2/idv/verify' \
-u '{{merchantApiKey}}:{{merchantSecret}}' \
-F 'firstName=Jamie' \
-F 'lastName=Ross' \
-F 'dob=05/14/1990' \
-F 'phone=+15145550142' \
-F 'email=jamie.ross@example.com' \
-F 'street=123 Example Street' \
-F 'city=Montreal' \
-F 'state=QC' \
-F 'postcode=H0H 0H0' \
-F 'country=CAN' \
-F 'nationality=CAN' \
-F 'documentTypes=PASSPORT' \
-F 'documentCountry=CAN' \
-F 'externalUserId=user-67890' \
-F 'flowId=flow_a1b2c3d4' \
-F 'groupingId=group_e5f6g7h8' \
-F 'ocrMatch=true' \
-F 'passportFront=@/path/to/passport.jpg' \
-F 'selfie=@/path/to/selfie.jpg'

Example 2: Driver's License (Canada)

curl -X POST 'https://api2.cycurid.com/v2/idv/verify' \
-u '{{merchantApiKey}}:{{merchantSecret}}' \
-F 'firstName=Taylor' \
-F 'middleName=Lee' \
-F 'lastName=Kim' \
-F 'dob=09/23/1987' \
-F 'phone=+16045550187' \
-F 'email=taylor.kim@example.com' \
-F 'street=123 Example Street' \
-F 'street2=Suite 100' \
-F 'city=Vancouver' \
-F 'state=BC' \
-F 'postcode=V0V 0V0' \
-F 'country=CAN' \
-F 'nationality=CAN' \
-F 'documentTypes=DRIVERS' \
-F 'documentCountry=CAN' \
-F 'externalUserId=user-12345' \
-F 'flowId=flow_a1b2c3d4' \
-F 'groupingId=group_e5f6g7h8' \
-F 'ocrMatch=true' \
-F 'driversFront=@/path/to/drivers_front.jpg' \
-F 'driversBack=@/path/to/drivers_back.jpg' \
-F 'selfie=@/path/to/selfie.jpg'

Example 3: Minimal Required Fields

curl -X POST 'https://api2.cycurid.com/v2/idv/verify' \
-u '{{merchantApiKey}}:{{merchantSecret}}' \
-F 'firstName=Casey' \
-F 'lastName=Nguyen' \
-F 'externalUserId=user-22222' \
-F 'flowId=flow_a1b2c3d4'

Response Format

Once a request passes validation and reaches the verification stage, it returns HTTP 200 — the success field in the response body indicates the outcome. If the sanctions or KYC verification service itself is unreachable, a non-200 error is returned instead (see Error Responses).

Success Response — Verification Passed

{
"success": true,
"metadata": {
"externalUserId": "user-67890",
"info": {
"firstName": "Jamie",
"lastName": "Ross",
"dob": "1990-05-14",
"country": "CAN"
},
"ocrMatch": true,
"documents": [
{ "idDocType": "PASSPORT", "country": "CAN" }
]
},
"documents_processed": 1,
"facemap_id": "69dd249e...",
"document_results": [
{
"document_type": "PASSPORT",
"face_match": true,
"similarity_score": 0.682,
"message": "Faces match"
}
],
"mrz_data": {
"mrz_data": {
"status": "SUCCESS",
"given_name": "JAMIE",
"surname": "ROSS",
"document_number": "AB1234567",
"issuer_code": "CAN",
"birth_date": "1990-05-14",
"expiry_date": "2034-11-23",
"sex": "F",
"mrz_type": "TD3"
},
"success": true
},
"ocr_comparison": {
"match": true,
"match_score": 100,
"field_results": {},
"mismatches": [],
"warnings": []
},
"kycResults": {
"status": 200,
"results": [],
"total": { "value": 0, "relation": "eq" },
"queryParams": { "name": "Jamie Ross", "country": "CAN" }
}
}
note

kycResults.queryParams reflects the flattened data actually used to query the sanctions database for this request (name, DOB, country, etc.) — useful for auditing what was screened.

Success Response — Sanctions Match Found

{
"success": false,
"message": "Sanctions screening failed — potential match found",
"kycResults": {
"status": 200,
"results": [
{
"id": "entity-id",
"caption": "Entity Name",
"schema": "Person",
"score": 0.85,
"match": true,
"datasets": ["us_ofac_sdn"]
}
],
"total": { "value": 1, "relation": "eq" }
}
}

Success Response — KYC Verification Failed

Once sanctions screening and the verification service have both run, the response is broken into a result object per check. The root success field is false if any check failed; each result object carries its own success flag and, on failure, an error message describing why.

{
"success": false,
"idvResult": {
"success": false,
"facematch": false,
"similarityScore": 0.42,
"error": "The selfie does not match the photo in the document. Please ensure the selfie is of the same person shown in the document."
},
"documentResult": {
"success": true
},
"ocrResult": {
"success": true,
"match": true
},
"kycResults": {
"success": true,
"results": [],
"total": { "value": 0, "relation": "eq" }
}
}

More than one check can fail on the same request. For example, a flow with document, OCR, and minimum-age checks enabled might return:

{
"success": false,
"idvResult": {
"success": true,
"facematch": true,
"similarityScore": 0.6550370454788208,
"sdkStatus": "declined",
"sessionId": "add505db-d95c-4e4c-ac36-b3479f299e49"
},
"documentResult": {
"success": false,
"error": "Failed to verify the barcode data on the driver's license",
"code": 2507,
"source": "PDF417"
},
"minimumAgeResult": {
"success": false,
"error": "Unable to compare age: date of birth could not be extracted from the document"
},
"kycResults": {
"success": true,
"results": [],
"total": { "value": 0, "relation": "eq" },
"queryParams": { "name": "Jamie Ross", "firstName": "Jamie", "lastName": "Ross" }
}
}

In this example the applicant's face matched (idvResult.success: true), but the driver's license barcode could not be verified (documentResult) and the date of birth could not be extracted for the age check — so the overall success is false and idvResult.sdkStatus is "declined".

Result Objects

ObjectPresent WhenDescription
idvResultAlwaysSelfie and face-match outcome. Includes facematch, similarityScore, and, on failure, an error message. May include sdkStatus and sessionId.
documentResultA document is submittedDocument verification outcome — expiry, issuing-country match, front/back consistency, and MRZ/barcode extraction. Runs regardless of the flow's ocrMatch setting. On failure, includes error, a numeric code, and a source (e.g. MRZ, PDF417) identifying which check failed.
ocrResultFlow has ocrMatch enabledOutcome of comparing OCR/MRZ-extracted document data (name, DOB, etc.) against the submitted applicant information.
minimumAgeResultFlow has a minimum or maximum age configuredOutcome of the age requirement check.
kycResultsAlwaysSanctions screening result — see Sanctions Match Found above.

Common Failure Reasons

Result ObjectExample error MessageCode
idvResultNo face detected in the provided document image2001
idvResultUnable to process the document image2002
idvResultThe provided document image is invalid or corrupted2003
idvResultRequired document file is missing from the request2403
idvResultThe provided document type is not supported2404
idvResultSelfie image is required2405
idvResultNo face detected in the selfie image2101
idvResultUnable to process the selfie image2102
idvResultThe provided selfie image is invalid or corrupted2103
idvResultThe selfie does not appear to be a live person2104
idvResultUnable to complete liveness check2105
idvResultThe selfie does not match the photo in the document2201
idvResultFace similarity score is below acceptable threshold2202
idvResultMultiple faces detected in the image2203
idvResultThe selfie does not match the photo on the submitted identity document2204
idvResultUser ID already exists for this merchant2306
idvResultUser already registered with this merchant2307
documentResultFailed to extract Machine Readable Zone from passport2501
documentResultNo Machine Readable Zone was detected on the passport2511
documentResultFailed to verify the barcode data on the driver's license2507
documentResultNo barcode was detected on the driver's license2512
documentResultDriver's license front and back images do not match2505
documentResultID card front and back images do not match2506
documentResultThe issuing country detected on the document does not match the declared document country2508
documentResultThe provided document has expired2509
ocrResultMRZ data does not match the submitted user information2502
ocrResultOCR extracted data does not match the submitted metadata2503
ocrResultOne or more fields from OCR do not match metadata2504
minimumAgeResultApplicant does not meet the age requirements2701
minimumAgeResultDate of birth could not be extracted from the document2703
kycResultsApplicant has been flagged by a sanctions screening check2702

Error Responses

These errors return non-200 status codes — either the request was rejected before processing (validation, auth, jurisdiction), or a downstream service was unreachable during processing.

400 — Empty Field

{
"success": false,
"message": "Validation failed",
"errors": ["state cannot be empty"]
}

400 — Validation Error

{
"success": false,
"message": "Validation failed",
"errors": ["firstName - First name is required"]
}

400 — flowId Missing

{
"success": false,
"message": "Validation failed",
"errors": ["flowId - flowId is required"]
}

404 — Flow Not Found

Returned when flowId doesn't exist or doesn't belong to your merchant account.

{
"success": false,
"message": "Flow not found"
}

404 — Group Not Found

Returned when groupingId doesn't exist or doesn't belong to your merchant account.

{
"success": false,
"message": "Group not found"
}

400 — Flow Not In Group

Returned when flowId is not a member of the group specified by groupingId.

{
"success": false,
"message": "Flow does not belong to the specified group"
}

400 — Missing Required Fields (Flow Config)

Returned when the submitted flowId is configured to require specific fields, document types, or files and the request is missing one or more of them. The errors list reflects whichever of the flow's requirements weren't met — for example, a flow requiring DOB, either an ID card or passport, and a selfie:

{
"success": false,
"message": "Required fields missing",
"errors": [
"Date of Birth is required",
"Document Type is required: ID Card, Passport",
"ID Card Front is required",
"Passport Front is required",
"Selfie is required"
]
}

Or, for a flow that only requires a document (driver's license or passport), the document's country, and a selfie:

{
"success": false,
"message": "Required fields missing",
"errors": [
"At least one document type is required: Drivers License, Passport",
"Document Country is required",
"Selfie is required"
]
}

400 — File Validation Error

{
"success": false,
"message": "File validation failed",
"errors": ["selfie file is empty"]
}

400 — Missing SSN

Returned when the flow's configuration requires SSN (globally or for the submitted country) and it wasn't provided.

{
"success": false,
"message": "Validation failed",
"errors": ["Social Security Number is required"]
}

401 — Missing or Invalid Authentication

Returned when the Authorization header is missing or malformed (neither valid Basic nor Bearer credentials).

{
"error": "Unauthorized: Missing or invalid authentication header"
}

401 — Invalid Credentials

Returned when the merchant key or secret doesn't match an account.

{
"message": "Invalid Merchant or Secret Key"
}

403 — Restricted Jurisdiction

{
"success": false,
"message": "Restricted jurisdiction",
"errors": ["Country of residence 'RUS' is a restricted jurisdiction"]
}
Restricted Jurisdictions

The following countries are always restricted and will return a 403 error:

Belarus (BLR), Burundi (BDI), Central African Republic (CAF), Cuba (CUB), Iran (IRN), Libya (LBY), Myanmar (MMR), North Korea (PRK), Russia (RUS), Somalia (SOM), South Sudan (SSD), Sudan (SDN), Syria (SYR), Ukraine (UKR), Venezuela (VEN), Zimbabwe (ZWE)

A flow can additionally mark specific countries as restricted via country overrides in the portal — in that case the error message ends with "...is a restricted jurisdiction for this flow" instead. See Flows & Groups.

501 — Unsupported Document Type

Returned when a requested document type isn't supported yet.

{
"success": false,
"message": "ID_CARD verification is coming soon for this document/country combination",
"errors": ["..."]
}

429 — Rate Limit Exceeded

{
"success": false,
"message": "You have exceeded your requests per minute limit."
}

Environment Variables

When using the Postman collection, configure these variables:

VariableDescriptionExample Value
baseUrlAPI base URLhttps://api2.cycurid.com
merchantApiKeyYour merchant API keyProvided in portal
merchantSecretYour merchant secretProvided in portal

Document Type Reference

Document TypeFront FieldBack FieldBack Required
PassportpassportFrontN/ANever
Driver's LicensedriversFrontdriversBackAs configured on the flow
ID CardidCardFrontidCardBackAs configured on the flow