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:
- Call the API directly — submit verification data to
POST /v2/idv/verifyyourself using your merchant credentials, as described on this page. - 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}}
- Username:
- 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
- Validation — field formats, empty values, required fields (configured per flow)
- 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)
- KYC Verification — document verification, face match, MRZ extraction, OCR comparison
- 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
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
| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | User's first name (English characters only) |
lastName | string | Yes | User's last name (English characters only) |
externalUserId | string | Yes | Unique identifier for the user in your system |
flowId | string | Yes | ID of the flow to run this verification against (max 36 characters). Created in the portal — see Flows & Groups. |
groupingId | string | No | ID 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. |
sessionId | string | No | Links this call to a session created via the Web SDK Link flow (max 255 characters). |
middleName | string | No | User's middle name (English characters only) |
dob | string | No | Date of birth in MM/DD/YYYY format |
phone | string | No | Phone number in international format (e.g., +16045551234) |
email | string | No | Email address |
street | string | No | Street address line 1 (English characters only) |
street2 | string | No | Street address line 2 (English characters only) |
city | string | No | City (English characters only) |
state | string | No | State or province code (e.g., BC, ON) (English characters only) |
postcode | string | No | Postal or ZIP code |
country | string | No | Country code (ISO 3166-1 alpha-3, e.g., CAN, USA) |
nationality | string | No | Nationality code (ISO 3166-1 alpha-3) |
documentTypes | string | No | Comma-separated list: PASSPORT, DRIVERS, ID_CARD |
documentCountry | string | Conditional | Country code for issued documents (ISO 3166-1 alpha-3). Required when documentTypes is provided. |
ssn | string | Conditional | Social Security Number. Required when the flow's configuration marks SSN as required (globally or for a specific country override). |
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.
| Field | Type | Required When | Description |
|---|---|---|---|
passportFront | file | documentTypes includes PASSPORT | Front side of passport |
driversFront | file | documentTypes includes DRIVERS | Front side of driver's license |
driversBack | file | documentTypes includes DRIVERS | Back side of driver's license |
idCardFront | file | documentTypes includes ID_CARD | Front side of ID card |
idCardBack | file | documentTypes includes ID_CARD | Back side of ID card |
selfie | file | Flow's biometric type is selfie file upload | Static user selfie for face matching |
- 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:
selfieis 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" }
}
}
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
| Object | Present When | Description |
|---|---|---|
idvResult | Always | Selfie and face-match outcome. Includes facematch, similarityScore, and, on failure, an error message. May include sdkStatus and sessionId. |
documentResult | A document is submitted | Document 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. |
ocrResult | Flow has ocrMatch enabled | Outcome of comparing OCR/MRZ-extracted document data (name, DOB, etc.) against the submitted applicant information. |
minimumAgeResult | Flow has a minimum or maximum age configured | Outcome of the age requirement check. |
kycResults | Always | Sanctions screening result — see Sanctions Match Found above. |
Common Failure Reasons
| Result Object | Example error Message | Code |
|---|---|---|
idvResult | No face detected in the provided document image | 2001 |
idvResult | Unable to process the document image | 2002 |
idvResult | The provided document image is invalid or corrupted | 2003 |
idvResult | Required document file is missing from the request | 2403 |
idvResult | The provided document type is not supported | 2404 |
idvResult | Selfie image is required | 2405 |
idvResult | No face detected in the selfie image | 2101 |
idvResult | Unable to process the selfie image | 2102 |
idvResult | The provided selfie image is invalid or corrupted | 2103 |
idvResult | The selfie does not appear to be a live person | 2104 |
idvResult | Unable to complete liveness check | 2105 |
idvResult | The selfie does not match the photo in the document | 2201 |
idvResult | Face similarity score is below acceptable threshold | 2202 |
idvResult | Multiple faces detected in the image | 2203 |
idvResult | The selfie does not match the photo on the submitted identity document | 2204 |
idvResult | User ID already exists for this merchant | 2306 |
idvResult | User already registered with this merchant | 2307 |
documentResult | Failed to extract Machine Readable Zone from passport | 2501 |
documentResult | No Machine Readable Zone was detected on the passport | 2511 |
documentResult | Failed to verify the barcode data on the driver's license | 2507 |
documentResult | No barcode was detected on the driver's license | 2512 |
documentResult | Driver's license front and back images do not match | 2505 |
documentResult | ID card front and back images do not match | 2506 |
documentResult | The issuing country detected on the document does not match the declared document country | 2508 |
documentResult | The provided document has expired | 2509 |
ocrResult | MRZ data does not match the submitted user information | 2502 |
ocrResult | OCR extracted data does not match the submitted metadata | 2503 |
ocrResult | One or more fields from OCR do not match metadata | 2504 |
minimumAgeResult | Applicant does not meet the age requirements | 2701 |
minimumAgeResult | Date of birth could not be extracted from the document | 2703 |
kycResults | Applicant has been flagged by a sanctions screening check | 2702 |
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"]
}
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:
| Variable | Description | Example Value |
|---|---|---|
baseUrl | API base URL | https://api2.cycurid.com |
merchantApiKey | Your merchant API key | Provided in portal |
merchantSecret | Your merchant secret | Provided in portal |
Document Type Reference
| Document Type | Front Field | Back Field | Back Required |
|---|---|---|---|
| Passport | passportFront | N/A | Never |
| Driver's License | driversFront | driversBack | As configured on the flow |
| ID Card | idCardFront | idCardBack | As configured on the flow |