Identity Verification
Identity verification uses Stripe Identity to verify a user’s identity through government-document submission and a live selfie. After Stripe approval, an automated NameScan check runs against PEP/sanctions databases before the user is marked as fully verified.
Table of Content:
Initiate verification
Create a new Stripe verification session for the authenticated user. If the user already has a pending verification session, the existing session is returned without creating a duplicate. 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 fq6JoEFTsxiXAl1cVdPDnK4emIQCwaUBfq6JoEFTsxiXAl1cVxPDnK4emIQCwaUB'
Returns
New or reset verification — returns a Stripe session for the user to complete:
{
"clientSecret": "vs_c_1234567890abcdef",
"sessionId": "vs_1234567890abcdef",
"status": "pending"
}
The clientSecret is passed to the Stripe Verify component in the frontend to render the document upload and selfie capture modal.
Existing pending session — returns the existing session (empty clientSecret):
{
"clientSecret": "",
"sessionId": "vs_1234567890abcdef",
"status": "pending"
}
Already verified — returns the current verification status:
{
"status": "passed",
"attempts": 1,
"maxAttempts": 3,
"expiresAt": "2026-06-15T10:23:41Z",
"verifiedAt": "2026-05-02T14:30:00Z",
"nameScanStatus": "passed"
}
Note: If the user’s verification status is
failedand they have exhausted all attempts, the endpoint returns HTTP403 Forbiddenwith the message “max verification attempts reached”.
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 fq6JoEFTsxiXAl1cVdPDnK4emIQCwaUBfq6JoEFTsxiXAl1cVxPDnK4emIQCwaUB'
Returns
A successful response will return a JSON object with verification status details:
No verification record exists:
{
"status": "none",
"attempts": 0,
"maxAttempts": 3,
"nameScanStatus": ""
}
Verification in progress or completed:
{
"status": "pending",
"attempts": 1,
"maxAttempts": 3,
"expiresAt": "2026-06-15T10:23:41Z",
"verifiedAt": null,
"nameScanStatus": "pending"
}
Verification completed with KYC data:
{
"status": "passed",
"attempts": 1,
"maxAttempts": 3,
"expiresAt": "2026-06-15T10:23:41Z",
"verifiedAt": "2026-05-02T14:30:00Z",
"nameScanStatus": "passed",
"country": "US",
"dateOfBirth": "1990-01-15T00:00:00Z",
"addressLine1": "123 Main St",
"addressLine2": "",
"city": "New York",
"state": "NY",
"postalCode": "10001",
"nationality": "USA"
}
KYC fields
After the user completes Stripe 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. |
| 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 | Stripe Identity check passed and NameScan cleared. User is fully verified. |
| failed | Verification failed (too many attempts, manual review rejection, or revoked). |
| expired | The verification session expired before completion. |
NameScan status values
| Status | Description |
|---|---|
| (empty) | No NameScan check has been run yet (no verification record exists). |
| pending | Stripe approved but NameScan has not yet run, or verification was reset. |
| passed | NameScan PEP/sanctions screening cleared — no hits found. |
| failed | NameScan found a PEP or sanctions match. User is NOT verified. |
Stripe webhook
Receives verification event notifications from Stripe. This endpoint is internal-only (not exposed through the public gateway) and validates the Stripe signature on every request.
Endpoint: https://apis.threatwinds.com/api/auth/v2/stripe/webhook
Handled events
| Event | Action |
|---|---|
verification.session.verified | Sets verification status, triggers NameScan screening, marks user as verified on completion. Publishes user.verified LCE event. |
verification.session.canceled | Records a failed attempt, increments attempt counter, marks as failed if max attempts reached. |
verification.session.requiring_input | Silently acknowledged — user is still in the verification flow. |
Any other event type returns HTTP 501 Not Implemented.
Note: This endpoint requires no authentication headers. Request authenticity is validated via the Stripe signature (
Stripe-Signatureheader). The endpoint is protected by Cloud Run--ingress internaland is not reachable from the public internet.
Error Response Headers
For responses with status codes other than 200 and 202, the following headers are included:
| Header | Description |
|---|---|
| x-error | Contains a description of the error that occurred |
| x-error-id | Contains a unique identifier for the error for 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 |
| 404 | Not Found | The requested resource does not exist |
| 500 | Internal Server Error | Server-side error; please contact support if persistent |