Handle Step-Up Verification (DocV and OTP) via API
Handle Document Verification (DocV), One-Time Passcode (OTP), and additional identity data step-up verification in your Prefill integration.
Detect additional verification
Use this guide to handle additional verification when an evaluation returns REVIEW and requires more input before RiskOS™ can return a terminal decision.
In this workflow, additional verification can take three forms:
- One-Time Passcode (OTP) — Verify control of the phone number
- Additional identity data — Collect more PII to continue the evaluation
- Predictive Document Verification (DocV) — Launch document capture and resume the evaluation asynchronously
For routing patterns and response interpretation, see Handle Results.
RiskOS™ pauses an evaluation when more input or verification is required.
Common signals:
| Verification type | How to detect it | What your application should do |
|---|---|---|
| OTP | decision = REVIEW, status = ON_HOLD, sub_status = Awaiting Customer OTP, tags include OTP Triggered | Prompt for the code and verify with PATCH /api/evaluation/{eval_id} |
| Additional identity data | decision = REVIEW, status = ON_HOLD, sub_status = More information needed, tags include Additional PII Requested | Collect identity data and continue with PATCH /api/evaluation/{eval_id} |
| Predictive DocV | decision = REVIEW, status = ON_HOLD or eval_status = evaluation_paused, and data_enrichments contains enrichment_provider = "SocureDocRequest" | Launch the DocV flow and wait for the final result |
Important:
Treat
REVIEWas a non-terminal state. After every OTP submission, identity update, or DocV handoff, re-evaluate the updated response to determine the next step.
Verification flow at a glance
REVIEW
├── Awaiting Customer OTP
│ └── Verify OTP with PATCH
│ ├── REVIEW → More information needed
│ ├── ACCEPT
│ └── REJECT
├── More information needed
│ └── Collect identity data and PATCH
│ ├── ACCEPT
│ └── REJECT
└── SocureDocRequest present
└── Launch DocV
└── Wait for webhook or updated evaluationHandle One-Time Passcode
Use this flow when the evaluation returns:
decision = REVIEWstatus = ON_HOLDsub_status = Awaiting Customer OTPtagscontainsOTP Triggered
What your application should do
- Display a 6-digit OTP input.
- Allow the user to retry if needed (up to five attempts).
- Submit the code using
PATCH /api/evaluation/{eval_id}. - Route the user based on the updated evaluation response.
If the maximum number of attempts is reached, the evaluation returns a final REJECT decision.
For more information on OTP-specific scenarios, see the OTP Integration Guide.
Verify the One-Time Passcode (PATCH)
PATCH)When the user enters the 6-digit code, your backend should verify it by sending a PATCH request to the Evaluation endpoint with the required top-level fields.
Example request
curl -X PATCH "https://riskos.sandbox.socure.com/api/evaluation/{eval_id}" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"id": "f0b3075b-0ae1-4130-9171-88d6c75a982c",
"timestamp": "2026-02-16T20:02:44.717Z",
"workflow": "non_hosted_advanced_pre_fill",
"data": {
"individual": {
"otp": {
"code": "622123"
}
}
}
}'{
"id": "f0b3075b-0ae1-4130-9171-88d6c75a982c",
"timestamp": "2026-02-16T20:02:44.717Z",
"workflow": "non_hosted_advanced_pre_fill",
"data": {
"individual": {
"otp": {
"code": "622123"
}
}
}
}data.individual.otp fields
data.individual.otp fields| Field | Action | Description |
|---|---|---|
code | Verify | 6-digit OTP entered by the user |
resend | Resend | Set to true to resend the code |
One-Time Passcode outcomes
After OTP submission, RiskOS™ returns an updated evaluation.
| Outcome | decision | status | What to do |
|---|---|---|---|
| OTP challenge still active | REVIEW | ON_HOLD | Prompt for OTP again and continue until the workflow resolves or retries are exhausted |
| OTP verified, more identity data required | REVIEW | ON_HOLD | Display prefilled data if available, collect remaining PII, and continue |
| Evaluation fails | REJECT | CLOSED | Route to fallback or decline handling |
| Evaluation completes | ACCEPT | CLOSED | Continue onboarding |
Tip:
Use tags such as
OTP Triggered,OTP Approved,Prefill Successful, andPrefill Unsuccessfulto determine what happened after OTP verification.
Example: One-Time Passcode challenge still active
{
"eval_id": "8d455113-94f8-4bc2-82f6-e016e078f5dc",
"decision": "REVIEW",
"status": "ON_HOLD",
"sub_status": "Awaiting Customer OTP",
"tags": [
"OTP Triggered"
]
}What to do:
- Prompt the user to enter the OTP.
- Allow retry if the code is incorrect (up to your configured limit).
- Submit the code using
PATCH /api/evaluation/{eval_id}. - Do not proceed until OTP is successfully verified or the evaluation fails.
Example: One-Time Passcode approved, more identity data required
{
"eval_id": "8d455113-94f8-4bc2-82f6-e016e078f5dc",
"decision": "REVIEW",
"status": "ON_HOLD",
"sub_status": "More information needed",
"tags": [
"OTP Triggered",
"OTP Approved",
"Prefill Successful",
"Approved to Display the Prefill Data",
"Additional PII Requested"
]
}What to do:
- If
Prefill SuccessfulandApproved to Display the Prefill Dataare present:- Display prefilled identity data to the user.
- Lock non-editable fields (such as
phone_numberanddate_of_birth).
- If Prefill is not available:
- Display a blank or partially completed form.
- Collect the remaining required identity fields.
- Submit the data using
PATCH /api/evaluation/{eval_id}to continue the evaluation.
Example: Final accept after One-Time Passcode flow
{
"eval_id": "8d455113-94f8-4bc2-82f6-e016e078f5dc",
"decision": "ACCEPT",
"status": "CLOSED",
"sub_status": "Accept",
"tags": [
"OTP Triggered",
"OTP Approved",
"Prefill Successful",
"Approved to Display the Prefill Data",
"Additional PII Requested"
]
}What to do:
- Treat this as a terminal success state.
- Continue onboarding (for example, create account, grant access).
- Do not prompt for additional verification.
Note:
Tags from earlier steps (such as
Additional PII Requested) may still be present in the final response. Usedecisionandstatusto determine the final outcome.
Example: Final reject
{
"eval_id": "8d455113-94f8-4bc2-82f6-e016e078f5dc",
"decision": "REJECT",
"status": "CLOSED",
"tags": [
"Phone - High Risk"
]
}What to do:
- Treat this as a terminal failure state.
- Stop onboarding.
- Route the user to your fallback, manual review, or decline flow.
- Optionally use
tagsto tailor messaging or internal handling.
Handle Additional Identity Data
Use this flow when the evaluation returns:
decision = REVIEWstatus = ON_HOLDsub_status = More information neededtagscontainsAdditional PII Requested
This may happen after OTP verification or when the workflow needs more identity data before it can return a final decision.
What your application should do
- If
tagscontainsPrefill SuccessfulandApproved to Display the Prefill Data, display the prefilled data to the user - If
tagscontainsPrefill Unsuccessful, display a blank or partially completed form - Collect the remaining identity data
- Submit the update using
PATCH /api/evaluation/{eval_id}
Prefill handling
If Prefill data is returned, do not allow users to edit:
phone_numberdate_of_birth
Note:
Prefill Successfuldoes not mean the evaluation is complete. The evaluation may still remain inREVIEWwithsub_status = More information neededuntil the required identity fields are submitted.
Example: Prefill successful, more PII still required
{
"decision": "REVIEW",
"status": "ON_HOLD",
"sub_status": "More information needed",
"tags": [
"OTP Triggered",
"OTP Approved",
"Prefill Successful",
"Approved to Display the Prefill Data",
"Additional PII Requested"
]
}What to do:
- Display the prefilled identity data to the user.
- Do not allow editing of locked fields (for example,
phone_numberanddate_of_birth). - Prompt the user to confirm or complete any missing fields.
- Collect the remaining required identity data.
- Submit the updated data using
PATCH /api/evaluation/{eval_id}to continue the evaluation.
Example: Prefill unsuccessful, more PII required
{
"decision": "REVIEW",
"status": "ON_HOLD",
"sub_status": "More information needed",
"tags": [
"OTP Triggered",
"OTP Approved",
"Prefill Unsuccessful",
"Additional PII Requested"
]
}What to do:
- Display a blank or partially completed identity form.
- Prompt the user to enter their full identity information.
- Collect all required fields (for example, name, address, and at least one additional identifier).
- Submit the data using
PATCH /api/evaluation/{eval_id}to continue the evaluation.
Note:
Prefill SuccessfulorPrefill Unsuccessfuldoes not indicate a final outcome. The evaluation remains inREVIEWuntil all required identity data is submitted and processed.
Continue the evaluation with identity data
When the workflow requires identity data, submit a PATCH request to continue the evaluation.
curl --request PATCH \
--url "https://riskos.sandbox.socure.com/api/evaluation/{eval_id}" \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"id": "f0b3075b-0ae1-4130-9171-88d6c75a982c",
"timestamp": "2026-03-11T13:24:43.000Z",
"workflow": "non_hosted_advanced_pre_fill",
"data": {
"individual": {
"given_name": "Jane",
"family_name": "Smith",
"date_of_birth": "1990-05-15",
"email": "[email protected]",
"phone_number": "+14155550001",
"address": {
"line_1": "123 Main St",
"locality": "Newark",
"major_admin_division": "NJ",
"postal_code": "07102",
"country": "US"
}
}
}
}'{
"id": "f0b3075b-0ae1-4130-9171-88d6c75a982c",
"timestamp": "2026-03-11T13:24:43.000Z",
"workflow": "non_hosted_advanced_pre_fill",
"data": {
"individual": {
"given_name": "Jane",
"family_name": "Smith",
"date_of_birth": "1990-05-15",
"email": "[email protected]",
"phone_number": "+14155550001",
"address": {
"line_1": "123 Main St",
"locality": "Newark",
"major_admin_division": "NJ",
"postal_code": "07102",
"country": "US"
}
}
}
}See Update an Evaluation for request structure and field guidance.
Handle Predictive Document Verification
Use this flow when the evaluation pauses and returns a DocV handoff request.
Detect the DocV step-up
Look for:
decision = REVIEWstatus = ON_HOLDoreval_status = evaluation_pauseddata_enrichmentscontains an object whereenrichment_provider = "SocureDocRequest"
Extract handoff assets
Locate the SocureDocRequest object and extract the following values from response.data:
| Field | Purpose |
|---|---|
docvTransactionToken | Required. Pass this to your frontend to initialize the DocV SDK |
url | Optional. Use this for direct mobile redirect flows |
qrCode | Optional. Use this for desktop-to-mobile handoff |
Example response
{
"decision": "REVIEW",
"status": "ON_HOLD",
"eval_status": "evaluation_paused",
"data_enrichments": [
{
"enrichment_provider": "SocureDocRequest",
"response": {
"data": {
"docvTransactionToken": "70c6a4bc-f646-4c6a-94c1-9cd428e356ef",
"url": "https://verify.socure.com/..."
}
}
}
]
}What to do:
- Locate the
SocureDocRequestobject indata_enrichments. - Extract
docvTransactionTokenfromresponse.data. - Persist the
eval_idanddocvTransactionTokenfor correlation with the final result. - Send the token to your frontend and launch the DocV flow.
- Wait for the final result via webhook or an updated evaluation response.
Launch the Document Verification Web SDK
The DocV Web SDK is a client-side JavaScript library that embeds the RiskOS™ document verification experience directly into your application.
It renders the Capture App, a secure mobile experience that guides the user through:
- Capturing a government-issued ID
- Taking a selfie
- Completing liveness verification
The Capture App UI is configurable from the DocV App page in the RiskOS™ Dashboard.
- Include the DocV Web SDK script in your application.
- Pass your SDK key (from the RiskOS™ Dashboard) and
docvTransactionTokentoSocureDocVSDK.launch().
<html>
<head>
<script src="https://websdk.socure.com/bundle.js"></script>
</head>
<body>
<button onclick="start()">Start Verification</button>
<div id="websdk"></div>
<script>
var config = {
onProgress: function (event) {
/* Called during capture */
},
onSuccess: function (response) {
/* Called on success */
},
onError: function (error) {
/* Called on error */
},
qrCodeNeeded: true, // Show QR code option
disableSmsInput: false, // Enable SMS input
closeCaptureWindowOnComplete: true, // Auto-close tab after document upload confirmation
autoOpenTabOnMobile: true, // Skip "Continue on New Tab" screen on mobile and launch Capture App in a new tab
};
function start() {
SocureDocVSDK.launch(
'SDKKey', // Replace with your SDK Key
'docvTransactionToken', // Replace with the docvTransactionToken
'#websdk',
config // optional
);
}
</script>
</body>
</html>Handoff options
Depending on your integration pattern, users can access the Capture App using:
- QR code for desktop-to-mobile handoff
- Secure SMS link for desktop or mobile
- Mobile redirect URL using
response.data.url
No resubmit: Treat DocV as a one-time step-up for the evaluation. If the step-up fails, RiskOS™ completes the evaluation and you should route the user to your fallback/manual flow (typically a final REJECT decision is delivered via webhook).
iOS and Android apps:
You can launch the Capture App by redirecting the user to the DocV URL returned in the Evaluation response.
data_enrichments[n].response.data.urlIf you include
data.individual.docv.config.redirect.urlin your Evaluation request, the user will be redirected back to your app after capture completes. See the integration guides for iOS Webview or Android Webview for advanced SDK configuration and mobile patterns.
Receive the final decision
DocV is asynchronous. After the Capture App flow completes, RiskOS™ resumes the paused evaluation and returns the final result.
Use one of the following approaches:
- Wait for an updated evaluation
- Receive the final result through a webhook event
To receive the final decision by webhook, configure a webhook endpoint on the Developer Workbench > Webhooks page in the RiskOS™ Dashboard.
Final routing
After DocV completes:
- Wait for the final
decision - Route based on the terminal response
- Do not re-submit DocV for the same evaluation
Routing outcomes:
ACCEPT→ Continue onboarding (for example, create account or grant access)REJECT→ Route to your fallback, manual review, or decline flow
Important:
Do not route on
REVIEW. This indicates the evaluation is still in progress.
Example routing logic
if (data.decision === "ACCEPT") {
router.push("/success");
}
if (data.decision === "REJECT") {
router.push("/review");
}Key takeaways
- Detect which verification flow the response requires.
- Use
PATCHfor OTP and identity data updates. - Use the DocV handoff assets to launch document capture.
- Wait for the updated or final decision before routing the user.
Updated 5 days ago
