Identity Verification
Identity verification validates a user’s identity through government-document submission and a live selfie. After identity check approval, an automated compliance screen runs against PEP and sanctions databases before the user is marked as fully verified.
Table of Content:
Initiate verification
Create a new verification session for the authenticated user. If the user already has a pending verification session, the previous session is canceled and a fresh one is created. If the user is already verified, the current status is returned.
Endpoint: https://apis.threatwinds.com/api/auth/v2/verify
Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | The bearer token for an active session. |
To initiate verification, use a POST request, for example:
curl -X 'POST' \
'https://apis.threatwinds.com/api/auth/v2/verify' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <YOUR_BEARER_TOKEN>'
Returns
HTTP 200 — new or reset verification — returns a session for the user to complete:
{
"clientSecret": "vs_c_1234567890abcdef",
"sessionId": "vs_1234567890abcdef",
"status": "pending",
"url": "https://verify.example.com/ixs_1234567890abcdef"
}
The clientSecret is passed to the verification component in the frontend to render the document upload and selfie capture modal. Alternatively, the url field provides a hosted verification page the user can be redirected to. The URL expires after 48 hours and can only be used once.
Already verified — returns the current verification status with full KYC data:
{
"status": "passed",
"attempts": 1,
"maxAttempts": 3,
"verifiedAt": "2026-05-02T14:30:00Z",
"expiresAt": "2026-06-15T10:23:41Z",
"country": "US",
"dateOfBirth": "1990-01-15T00:00:00Z",
"addressLine1": "123 Main St",
"addressLine2": "",
"city": "New York",
"state": "NY",
"postalCode": "10001",
"nationality": "USA",
"attemptsLog": [
{
"createdAt": "2026-05-02T14:30:00Z",
"status": "passed"
}
]
}
Note: The
attemptsLogincludes up to 5 most recent attempts. ThelastFailedReasonfield may also be present if there were recent failures.
Note: If the user’s verification status is
failedand they have exhausted all attempts, the endpoint returns HTTP403 Forbiddenwith a structured body:{ "code": "max_attempts_reached", "message": "You have used all verification attempts", "attempts": 3, "maxAttempts": 3, "lastFailedReason": "Compliance screening hit found: pep=1, sip=0" }
Get verification status
Retrieve the current identity verification status for the authenticated user, including attempt count and expiration details.
Endpoint: https://apis.threatwinds.com/api/auth/v2/verify/status
Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| Authorization | header | string | Yes | The bearer token for an active session. |
To get the verification status, use a GET request, for example:
curl -X 'GET' \
'https://apis.threatwinds.com/api/auth/v2/verify/status' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <YOUR_BEARER_TOKEN>'
Returns
A successful response will return a JSON object with verification status details:
No verification record exists:
{
"status": "none",
"attempts": 0,
"maxAttempts": 3
}
Verification in progress:
{
"status": "pending",
"attempts": 1,
"maxAttempts": 3,
"lastFailedReason": "The document is expired.",
"expiresAt": "2026-06-15T10:23:41Z",
"verifiedAt": null,
"attemptsLog": [
{
"createdAt": "2026-06-10T14:30:00Z",
"status": "failed",
"failedReason": "The document is expired."
}
]
}
Verification completed with KYC data:
{
"status": "passed",
"attempts": 1,
"maxAttempts": 3,
"verifiedAt": "2026-05-02T14:30:00Z",
"expiresAt": "2026-06-15T10:23:41Z",
"country": "US",
"dateOfBirth": "1990-01-15T00:00:00Z",
"addressLine1": "123 Main St",
"addressLine2": "",
"city": "New York",
"state": "NY",
"postalCode": "10001",
"nationality": "USA",
"attemptsLog": [
{
"createdAt": "2026-05-02T14:30:00Z",
"status": "passed"
}
]
}
KYC fields
After the user completes identity verification, the following additional fields are populated from the verified government document. These fields are empty (null or "") until verification is complete:
| Field | Type | Description |
|---|---|---|
| country | string | Issuing country of the document (ISO 3166-1 alpha-2). |
| dateOfBirth | string | Date of birth from the document (RFC 3339). Nullable — may be null if the document does not contain a date of birth. |
| addressLine1 | string | First line of the address from the document. |
| addressLine2 | string | Second line of the address from the document. |
| city | string | City from the document. |
| state | string | State/province from the document. |
| postalCode | string | Postal/zip code from the document. |
| nationality | string | Nationality from the document (ISO 3166-1 alpha-3). |
Status values
| Status | Description |
|---|---|
| none | No verification session has been created for this user. |
| pending | A verification session is active and awaiting document/selfie submission. |
| passed | Identity check passed and compliance screening cleared. User is fully verified. |
| failed | Verification failed (too many attempts, compliance hit, revoked, or failed check). |
Attempt log
The attemptsLog array provides the history of recent verification attempts (up to 5 most recent, newest first). Each entry contains:
| Field | Type | Description |
|---|---|---|
| createdAt | string | When the attempt was recorded (RFC 3339). |
| status | string | "passed", "failed", or "canceled". |
| failedReason | string | Why the attempt failed (present when status is "failed" or "canceled"). |
Common failure reasons from the identity provider:
code — reason | Cause |
|---|---|
document_expired — "The document is expired." | The submitted ID document has expired |
document_selfie_face_comparison_failed — "The selfie did not match the document." | The live selfie photo did not match the ID photo |
document_unclear — "The document photo was unclear." | The document photo quality was insufficient |
compliance_hit — "Compliance screening hit found: pep=X, sip=Y" | Compliance screening detected a PEP or sanctions match |
user_canceled — "user canceled verification" | The user canceled the verification before completing it |
Note: Canceling a verification burns an attempt toward the maximum. If a user has exhausted all attempts through cancellations or failures, they must contact an administrator to reset their verification.
Note: The
lastFailedReasonfield may be empty if the most recent attempt was successful, was canceled, or the failure reason was not captured.
Error Response Headers
For responses with status codes other than 200 and 202, the following headers are included:
| Header | Description |
|---|---|
| x-error | Human-readable error message describing what went wrong |
| x-error-id | Unique identifier for error tracking and support |
Error Codes
| Status Code | Description | Possible Cause |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or malformed JSON |
| 401 | Unauthorized | Missing or invalid authentication credentials |
| 403 | Forbidden | Max verification attempts reached (body includes lastFailedReason) |
| 404 | Not Found | The requested resource does not exist |
| 500 | Internal Server Error | Server-side error; please contact support if persistent |