Skip to main content

Applicant Lookup API

Once a user has verified, these two endpoints let you look up what you know about them — their verification history, the data extracted from their documents, and their current status.

  • Resolve Facemap ID — turn your own user identifier into a facemap ID.
  • Get Applicant Data — everything recorded against one facemap.

Base URL

https://api2.cycurid.com

Authentication

Both endpoints use Basic Authentication with your merchant credentials:

  • Username: {{merchantApiKey}}
  • Password: {{merchantSecret}}
Authorization: Basic base64(merchantApiKey:merchantSecret)

Every lookup is scoped to your own merchant account — you can only read applicants that verified against your flows.

Facemaps, Users, and Registrations

Three ideas underpin both responses, and mixing them up is the most common source of confusion:

TermWhat it is
Facemap IDThe person. A biometric identifier that stays stable across every verification that face performs.
externalUserIdYour label for that person, within one group. Not a person — the same person carries a different one in each group they verify into.
RegistrationOne person in one group: an externalUserId + groupingId pair, with its own status, its own attempts, and its own extracted data.

So one facemap can hold several registrations. A user who verifies into two groups produces two registrations of the same face, each with its own status. Within a single group, one face can only register once — a second attempt to register the same face under the same group is rejected as a duplicate.

This is why neither response puts anything person-shaped at the top level: a single externalUserId or set of extracted fields up there could only describe one of the registrations, and you'd have no way to tell which.


Resolve Facemap ID

GET /v2/reports/applicants/byExternalUserId/:externalUserId

Turns one of your user identifiers into the facemap ID that identifies the person behind it. Use this first when you only have your own user ID, then pass the result to Get Applicant Data.

Path Parameters

FieldTypeRequiredDescription
externalUserIdstringYesThe identifier you sent as externalUserId on /v2/idv/verify. URL-encode it if it contains reserved characters. Maximum 500 characters.

The identifier may also be passed as a query parameter instead of a path segment, which avoids encoding issues:

GET /v2/reports/applicants/byExternalUserId?externalUserId=user-12345

Example

curl 'https://api2.cycurid.com/v2/reports/applicants/byExternalUserId/user-12345' \
-u '{{merchantApiKey}}:{{merchantSecret}}'

Response

{
"externalUserId": "user-12345",
"facemapId": "6a7e334fb881b8010f7d1ffc",
"applicants": [
{
"facemapId": "6a7e334fb881b8010f7d1ffc",
"groupingId": "group_e5f6g7h8",
"status": "approved",
"registered": true,
"createdAt": "2026-08-13 14:13:48",
"lastActive": "2026-08-14 09:22:55"
}
]
}
FieldDescription
facemapIdThe most recently active facemap — the one to pass to Get Applicant Data.
applicants[]One entry per (facemap, group) pair, most recently active first.
applicants[].registeredfalse means the facemap appears only in the verification logs and has no entry in the user mapping yet. status is null in that case.
applicants[].statusSee Status Values.
One user, one face

An externalUserId is pinned to a single facemap within your merchant account, so in a healthy dataset applicants[] contains exactly one distinct facemapId. Seeing more than one is worth investigating — it usually means the user was deleted and re-registered against a different face.


Get Applicant Data

GET /v2/reports/applicants/:facemapId

Returns everything recorded against one facemap, broken down by registration.

Path Parameters

FieldTypeRequiredDescription
facemapIdstringYesThe facemap ID, as returned by Resolve Facemap ID.

Example

curl 'https://api2.cycurid.com/v2/reports/applicants/6a7e334fb881b8010f7d1ffc' \
-u '{{merchantApiKey}}:{{merchantSecret}}'

Response

{
"facemapId": "6a7e334fb881b8010f7d1ffc",
"createdAt": "2026-08-13 14:12:47",
"merchantId": "1",
"registrationCnt": 2,
"registrations": [
{
"externalUserId": "user-12345",
"groupingId": "group_e5f6g7h8",
"status": "approved",
"createdAt": "2026-08-13 14:13:48",
"lastActive": "2026-08-14 09:22:55",
"flowId": "flow_a1b2c3d4",
"flowName": "Standard Onboarding",
"requiredDocuments": ["PASSPORT", "DRIVERS", "ID_CARD"],
"info": {
"firstName": "CHRISTIAN",
"lastName": "SMITH",
"dob": "07/16/1989",
"phone": "+15145551234",
"email": "christian.smith@example.com",
"country": "CAN",
"nationality": "CAN",
"addresses": [
{
"street": "200 Rue Sainte-Catherine Ouest",
"town": "Montreal",
"state": "QC",
"postCode": "H2X 1L1",
"country": "CAN"
}
],
"idDocs": [
{
"idDocType": "DRIVERS",
"country": "CAN",
"number": "S1234-56789-01234",
"validUntil": "2028-09-04",
"firstName": "CHRISTIAN",
"lastName": "SMITH",
"dob": "07/16/1989"
}
]
},
"verifications": [
{
"logId": 571,
"createdAt": "2026-08-14 09:22:55",
"status": "approved",
"success": true,
"reasons": [],
"flowId": "flow_a1b2c3d4",
"flowName": "Standard Onboarding",
"documentTypes": "DRIVERS",
"sessionId": "4a98055c-4398-42fe-ad37-578dc1310b22"
}
]
},
{
"externalUserId": "user-67890",
"groupingId": "group_x1y2z3w4",
"status": "pending",
"createdAt": "2026-08-13 14:12:47",
"lastActive": "2026-08-13 14:12:47",
"flowId": "flow_a1b2c3d4",
"flowName": "Standard Onboarding",
"requiredDocuments": ["PASSPORT"],
"info": { "...": "..." },
"verifications": [{ "...": "..." }]
}
]
}

Top-Level Fields

FieldDescription
facemapIdThe facemap you asked for.
createdAtWhen this face was first seen, across all registrations.
merchantIdYour merchant account ID.
registrationCntNumber of entries in registrations.
registrations[]One entry per registration, most recently active first.

Registration Fields

FieldDescription
externalUserIdYour identifier for this person in this group.
groupingIdThe group this registration belongs to. null if the verification was submitted without one.
statusSee Status Values.
createdAt / lastActiveFirst and most recent activity for this registration.
flowId / flowNameThe flow used on the most recent attempt.
requiredDocumentsDocument types this registration's flow asked for.
infoApplicant data — see The info Object.
verifications[]Every attempt on this registration, newest first.

Verification Fields

FieldDescription
logIdIdentifier for this attempt, usable with the portal's verification log.
createdAtWhen the attempt was submitted.
statusThe outcome of this single attempt — see Status Values.
successWhether the attempt passed.
reasonsWhy it did not pass, in the pipeline's own words. Empty on a clean attempt.
documentTypesDocuments submitted on this attempt.
sessionIdThe Web SDK session, if the attempt came through a link.

reasons collects the message from every stage that recorded one — liveness and face match, document authenticity, data extraction, the age rule, and sanctions screening — because any of them can sink an attempt:

"reasons": [
"Liveness Detection Failure: The selfie does not appear to be a live person. Please retake your selfie in good lighting.",
"No MRZ detected in the provided image"
]

An empty reasons on a failed attempt means no stage stored a message. Nothing is invented to fill the gap.

The info Object

info is assembled from the applicant data submitted with this registration's attempts, newest first per field — so a later attempt that omitted a field inherits it from an earlier one. Fields that were never submitted are omitted rather than returned as null.

info.idDocs is different: it holds what was read off the document by MRZ and OCR. Those names and dates can legitimately differ from the ones the applicant typed into the form, and that difference is often the interesting part.


Status Values

status appears on a registration and on each individual verification. The values are the same, but the scope differs: on a verification it is the outcome of that one attempt; on a registration it is the person's standing overall.

ValueMeaning
approvedVerification passed, or a reviewer approved it.
declinedA person declined this applicant from the portal.
failedThe verification pipeline rejected the attempt — bad document, failed liveness, and so on.
pendingSubmitted and awaiting a manual review decision.
nullRegistered, but no verification attempt recorded yet.
declined vs failed

These are different events. declined is a human decision; failed is an automated outcome. If you are deciding whether to let someone retry, failed usually means "try again", while declined usually does not.

A registration's status is not simply the newest attempt's status. The most recent real decision wins — a failed retry after an approval does not un-approve anyone — and an unresolved attempt outranks a failure, because someone who failed and then retried is waiting rather than failed. Only a registration with nothing but failures reads failed.

Error Responses

400 — Validation Error

{
"code": 400,
"description": "Validation failed",
"errors": ["externalUserId - Required"]
}

404 — Not Found

{
"code": 404,
"description": "No applicant found for externalUserId user-12345"
}

500 — Lookup Failed

{
"code": 500,
"description": "Applicant lookup for externalUserId user-12345 was incomplete: SDK registration lookup failed. Cannot confirm whether this applicant exists."
}
Treat 500 as "unknown", not "absent"

A 404 means the applicant does not exist. A 500 means a source could not be read, so their existence could not be determined — never treat it as absence. Re-onboarding a user who already exists will be rejected as a duplicate face.

Next Steps