End-to-End DocV Integration
End-to-end examples for all Predictive Document Verification (DocV) flows—Document Verification, Secondary Document Capture, Selfie Intelligence, and Selfie Reverification—covering the Evaluation API, SDK/Hosted launch, webhooks, and final decision.
Before you begin
Get your API key and SDK key from the API & SDK Keys page in the RiskOS™ Dashboard.
Configure a workflow with a Document Request step to trigger DocV as a step-up enrichment.
Create and publish a Capture App flow in the RiskOS™ Dashboard for your target flow type (Document Verification, Secondary Document Capture, Selfie Intelligence, or Selfie Reverification).
Configure a webhook endpoint to receive evaluation_completed and case_notes_added events.
Integrate the Predictive DocV SDK (for native SDK flows) or configure a Hosted Flow (for web-based flows).
Note:
This guide uses Sandbox endpoints. Replace
riskos.sandbox.socure.comwithriskos.socure.comwhen you move to production.
Choose a flow type
Predictive DocV supports four flow types. Each follows the same integration pattern (evaluate → step-up → capture → webhook → decision) but differs in what the consumer captures and what the enrichment response contains.
| Flow type | What the consumer captures | Enrichment response object | When to use |
|---|---|---|---|
| Document Verification | Government-issued ID + selfie | documentVerification | Onboarding, KYC, full identity verification |
| Secondary Document Capture | Supporting documents (utility bills, bank statements, etc.) | secondaryDocument | Proof of address, compliance, fraud review |
| Selfie Intelligence | Live selfie only | selfieIntelligence | Age checks, liveness verification, trust & safety |
| Selfie Reverification | Live selfie compared to a prior DocV headshot | selfieReverification | Returning user re-authentication, high-risk transactions |
Choose an integration method
For each flow type, you can launch the capture experience using a native SDK or the Hosted Capture App:
| Method | Description | Best for |
|---|---|---|
| Native SDK (iOS, Android, React Native, Web) | Embed the DocV capture UI directly in your app using the DocV SDK. | Mobile-first apps that need a tightly integrated experience. |
| Hosted Capture App (URL / QR / SMS) | Redirect the consumer to Socure's hosted web-based Capture App via URL, QR code, or SMS link. No SDK integration required. | Web-first flows, desktop-to-mobile handoff, or when you prefer a no-SDK approach. |
Both methods use the same Evaluation API call and return the same webhook events and enrichment responses. The difference is in Step 4 — how the consumer accesses the capture flow.
Integration steps
All four DocV flow types follow the same seven-step pattern. Flow-specific differences are called out inline.
sequenceDiagram
participant App as Your App
participant RiskOS as RiskOS™
participant DocV as DocV Capture
participant Consumer
App->>RiskOS: POST /api/evaluation (PII + docv.config)
Note right of RiskOS: Evaluation pauses at<br/>Document Request step
RiskOS-->>App: decision: REVIEW, status: ON_HOLD<br/>docvTransactionToken, url, qrCode
alt Native SDK (iOS, Android, React Native, Web)
App->>DocV: Launch SDK with docvTransactionToken
DocV->>Consumer: Present capture UI
else Hosted Capture App (URL / QR / SMS)
App->>Consumer: Send capture link (URL, QR, or SMS)
Consumer->>DocV: Open Capture App
end
alt Document Verification
Consumer->>DocV: ID front + ID back + selfie
else Secondary Document Capture
Consumer->>DocV: Upload supporting document
else Selfie Intelligence / Selfie Reverification
Consumer->>DocV: Capture live selfie
end
DocV->>RiskOS: Submit captured media
Note right of RiskOS: Workflow resumes
loop During capture
RiskOS-->>App: Webhook: case_notes_added
end
RiskOS-->>App: Webhook: evaluation_completed (ACCEPT / REJECT)
App->>App: Route consumer based on decision
Step 1: Configure the DocV flow
Create and publish a Capture App flow for your target flow type in the RiskOS™ Dashboard under Settings > DocV App > Flow. Each flow type presents a different capture experience to the consumer:
| Flow type | Capture screens |
|---|---|
| Document Verification | Splash → Consent → ID front → ID back → Selfie → Confirmation |
| Secondary Document Capture | Splash → Consent → Document upload (camera or file) → Preview → Confirmation |
| Selfie Intelligence | Splash → Consent → Selfie capture → Selfie preview → Confirmation |
| Selfie Reverification | Consent → Selfie capture → Selfie preview → Confirmation |
After publishing, note the use_case_key assigned to your flow. You pass this value in the API call to select the correct flow. If you omit use_case_key, RiskOS™ uses the flow marked as Default.
See Capture App configuration for setup instructions, or configure a Hosted Flow for a web-based experience.
Step 2: Call the evaluation API
Submit consumer PII to the RiskOS™ evaluation endpoint. Include the docv configuration object to specify your Capture App flow settings.
curl --location 'https://riskos.sandbox.socure.com/api/evaluation' \
--header 'accept: application/json' \
--header 'authorization: Bearer YOUR_API_KEY' \
--header 'content-type: application/json' \
--data-raw '{
"id": "APP-123456",
"timestamp": "2025-07-31T15:00:10.761Z",
"workflow": "consumer_onboarding",
"data": {
"individual": {
"given_name": "John",
"family_name": "Smith",
"date_of_birth": "1989-05-07",
"phone_number": "+19998887777",
"address": {
"line_1": "1234 N College Ave",
"locality": "New York City",
"major_admin_division": "NY",
"country": "US",
"postal_code": "10001"
},
"docv": {
"config": {
"document_type": "license",
"send_message": true,
"language": "en-us",
"redirect": {
"method": "POST",
"url": "https://example.com/docv"
}
}
}
}
}
}'| Field | Type | Required | Description | Example |
|---|---|---|---|---|
docv.config.document_type | String | Optional | Document type to capture. | "license" or "passport" |
docv.config.use_case_key | String | Optional | Specifies the Capture App flow. If omitted, RiskOS™ uses the flow marked as Default. | "my_docv_flow" |
The following fields are common to all flow types:
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
id | String | Required | Customer-defined unique identifier for the request. Must be unique per evaluation. Reuse may cause re-run behavior and impact results. | "APP-123456" |
workflow | String | Required | The name of the RiskOS™ workflow to execute. | "consumer_onboarding" |
docv.config.send_message | Boolean | Optional | Set to true to send an SMS link to the consumer. | true |
docv.config.language | String | Optional | Language code for the Capture App. | "en-us" |
docv.config.redirect | Object | Optional | Where to redirect the consumer after capture completes. | { "method": "POST", "url": "https://..." } |
Note:
If
use_case_keycontains an invalid value, the SDK returns anInvalid Requesterror.
Step 3: Receive the docvTransactionToken
docvTransactionTokenWhen the workflow includes a Document Request step, RiskOS™ pauses the evaluation and returns a response with these indicators:
decisionis"REVIEW"statusis"ON_HOLD"eval_statusis"evaluation_paused"data_enrichmentscontains an object whereenrichment_provideris"SocureDocRequest"
{
"id": "APP-123456",
"workflow": "consumer_onboarding",
"eval_id": "d48bcc0e-ab3c-43a4-b03e-67208458ec62",
"decision": "REVIEW",
"status": "ON_HOLD",
"eval_status": "evaluation_paused",
"data_enrichments": [
{
"enrichment_provider": "SocureDocRequest",
"status_code": 200,
"response": {
"data": {
"docvTransactionToken": "1e89eec0-f7a3-4210-b74b-a76bc9556c50",
"url": "https://verify.socure.com/#/dv/1e89eec0-f7a3-4210-b74b-a76bc9556c50",
"qrCode": "data:image/png;base64,iVBORw0KGgo..."
},
"referenceId": "3639f9ad-e1fe-4e3e-9202-18faf20ace56"
}
}
]
}Extract the handoff assets from data_enrichments[n].response.data:
| Field | Type | Description | Example |
|---|---|---|---|
docvTransactionToken | String | Always returned. Pass this token to the DocV SDK to start document capture. | "1e89eec0-f7a3-4210-b74b-a76bc9556c50" |
url | String | Direct URL to the Capture App. Use for Hosted flows or redirect flows. | "https://verify.socure.com/#/dv/1e89eec0-f7a3-4210-b74b-..." |
qrCode | String | Base64-encoded PNG for desktop-to-mobile handoff (Hosted and web flows). May not be returned. | "data:image/png;base64,iVBORw0KGgo..." |
Note:
Persist the
eval_idanddocvTransactionTokenin your backend. You need theeval_idto correlate the webhook result with this evaluation.
This response is identical for all four flow types. The use_case_key you specified in Step 2 determines what the consumer sees during capture.
Step 4: Launch the capture experience
Pass the docvTransactionToken to the DocV SDK or redirect the consumer to the Hosted Capture App. The use_case_key from Step 2 controls which capture flow the consumer sees — the launch code is the same regardless of flow type.
func startDocVCapture(token: String) {
let options = SocureDocVOptions(
publicKey: "YOUR_SDK_KEY",
docVTransactionToken: token,
presentingViewController: self,
useSocureGov: false
)
SocureDocVSDK.launch(options) { (result: Result<SocureDocVSuccess, SocureDocVFailure>) in
switch result {
case .success(let success):
print("Capture complete. Session token: \(success.deviceSessionToken ?? "N/A")")
case .failure(let failure):
print("Capture failed: \(failure.error)")
}
}
}| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
publicKey | String | Required | The SDK key obtained from the RiskOS™ Dashboard, used to authenticate and initialize the DocV SDK. | "YOUR_SDK_KEY" |
docVTransactionToken | String | Required | Token from the evaluation response. | "1e89eec0-..." |
presentingViewController | UIViewController | Required | The view controller that presents the SDK. | self |
useSocureGov | Bool | Optional | Set to true for GovCloud. Defaults to false. | false |
Note:
After the capture experience launches, the consumer completes document capture without further backend interaction. RiskOS™ automatically resumes the paused evaluation when capture finishes or the session expires.
Step 5: Consumer completes verification
The consumer interacts with the capture UI presented by the SDK or Hosted Capture App. No backend action is required during this step — the SDK handles the entire capture flow based on the use_case_key.
| Flow type | What the consumer captures |
|---|---|
| Document Verification | Front and back of a government-issued ID, then a selfie |
| Secondary Document Capture | Supporting document (camera capture or file upload) |
| Selfie Intelligence | Live selfie |
| Selfie Reverification | Live selfie (compared to a prior DocV headshot) |
Step 6: Handle webhooks
RiskOS™ sends webhook events to your configured endpoint as the capture progresses and when the evaluation completes. See DocV webhooks for the full event reference.
Capture progress events (case_notes_added)
case_notes_added)These lightweight events track capture milestones. They do not contain PII.
| Event | Applicable flow types | Description |
|---|---|---|
Process Initiated | All | Consumer has been redirected to the Capture App. |
Capture App Opened | All | Consumer opened the Capture App. |
Document Front Uploaded | Document Verification | Front image of the document has been uploaded. |
Document Back Uploaded | Document Verification | Back image of the document has been uploaded. |
Document Selfie Uploaded | Document Verification, Selfie Intelligence, Selfie Reverification | Selfie image has been uploaded. |
Documents Upload Successful | All | All required images have been uploaded. |
Session Complete | All | The capture session has completed. |
Consent Declined | All | The consumer declined consent (terminal). |
Session Expired | All | The capture session expired (terminal). |
Note:
Filter
case_notes_addedevents by checking thatdata.reviewer_idequals"Webhook". This excludes notes added by manual reviewers.
Final evaluation result (evaluation_completed)
evaluation_completed)After capture and verification complete, RiskOS™ delivers the final decision via the evaluation_completed webhook. The top-level data.decision (uppercase: ACCEPT, REVIEW, REJECT) represents the workflow decision. The enrichment-specific result is nested inside data.data_enrichments.
{
"event_type": "evaluation_completed",
"event_id": "3b31289c-2d4a-4107-80bc-dda63031d5a0",
"event_at": "2025-07-17T01:20:01Z",
"data": {
"eval_id": "11111111-2222-3333-4444-555555555555",
"id": "client-transaction-12345",
"workflow": "consumer_onboarding",
"eval_status": "evaluation_completed",
"decision": "ACCEPT",
"status": "CLOSED",
"sub_status": "Accept",
"data_enrichments": [
{
"enrichment_provider": "SocureDocRequest",
"response": {
"status": "SESSION_COMPLETE",
"data": {
"docvTransactionToken": "7d6ad42b-f804-4255-b25e-268b8a77c86f",
"url": "https://verify.socure.com/#/dv/7d6ad42b-f804-4255-b25e-268b8a77c86f"
}
}
}
]
}
}The enrichment-specific response object varies by flow type and is covered in Step 7.
Step 7: Process the final decision
Match the eval_id from the webhook payload to the evaluation you stored in Step 3, then route the consumer based on the top-level decision:
| Decision | Action |
|---|---|
ACCEPT | The consumer passed verification. Grant access or proceed with onboarding. |
REJECT | The consumer failed verification. Deny access or escalate for manual review. |
REVIEW | The evaluation requires manual review. Do not treat this as a final decision — it indicates an intermediate state. |
function handleEvaluationWebhook(payload: EvaluationCompletedEvent) {
const { eval_id, decision } = payload.data;
// Correlate with the stored evaluation
const evaluation = lookupEvaluation(eval_id);
if (decision === "ACCEPT") {
completeOnboarding(evaluation.userId);
}
if (decision === "REJECT") {
denyAccess(evaluation.userId);
}
}Enrichment response by flow type
The data_enrichments array in the evaluation_completed webhook contains the flow-specific enrichment result. The enrichment object name and structure differ by flow type:
The enrichment response contains a documentVerification object:
{
"enrichment_provider": "Socure",
"enrichment_name": "Socure Document Verification",
"response": {
"referenceId": "ed6a5077-b272-4a75-8c21-b284e10927cd",
"documentVerification": {
"decision": {
"name": "standard",
"value": "accept"
},
"reasonCodes": ["I831", "I836"],
"documentType": {
"type": "Drivers License",
"country": "USA",
"state": "NY"
}
}
}
}| Field | Type | Description | Example |
|---|---|---|---|
decision | Object | The DocV decision (accept, review, reject, or resubmit). | { "name": "standard", "value": "accept" } |
reasonCodes | Array | Reason codes explaining the decision. | ["I831", "I836"] |
documentType | Object | Details about the captured document (type, country, state). | { "type": "Drivers License", "country": "USA", "state": "NY" } |
View all reason codes:
For the complete catalog of reason codes and their descriptions, open the Reason Codes page in the RiskOS™ Dashboard.
Handle resubmit and cancel scenarios
Resubmit: If the enrichment returns a resubmit decision, the workflow may route to a resubmit outcome. Start a new evaluation for the consumer. Tailor the user-facing message based on the reasonCodes returned (for example, prompting the consumer to use a different document type or improve lighting conditions). Limit retry attempts to prevent infinite loops.
Cancel: If the capture session expires (Session Expired) or the consumer declines consent (Consent Declined), the workflow resumes with a terminal-negative outcome. You can configure your workflow to automatically cancel the evaluation using a Condition step or Decision Rules step — no manual intervention required.
Re-attempt after expiration: When a capture session expires, RiskOS™ can generate a new docvTransactionToken within the same evaluation. Configure your workflow to trigger a re-attempt after Session Expired instead of canceling, which preserves the original evaluation context.
Note:
You can automate both Cancel and Resubmit decisions in your workflow. See DocV Webhooks for details on how
case_notes_addedevents interact with workflow execution.
Integration checklist
After completing your integration, confirm the following in your Sandbox environment:
All flow types
decision: "REVIEW", status: "ON_HOLD", and eval_status: "evaluation_paused" with a SocureDocRequest enrichmenteval_id and docvTransactionTokencase_notes_added events during captureevaluation_completed event with a final decisionACCEPT and REJECT decisionsFlow-specific checks
documentVerification objectGET /api/evaluation/{eval_id}/documentsselfieIntelligence object with predictedAgeprevious_reference_id is included in the request, and the enrichment response contains a selfieReverification object with the previousReferenceIdUse DocV Sandbox testing to simulate specific verification outcomes by passing predefined docvTransactionToken values.
Troubleshooting
See DocV Troubleshooting & FAQs for a full list of FAQs and troubleshooting scenarios.
| Symptom | Cause | Resolution |
|---|---|---|
Evaluation returns decision: "REVIEW" but no SocureDocRequest in data_enrichments | The workflow does not include a Document Request step. | Add a Document Request step to your workflow in the RiskOS™ dashboard. |
SDK returns invalidDocvTransactionToken error | The token has expired or was already consumed. | Generate a new token by calling the evaluation API again. Tokens are single-use. |
No evaluation_completed webhook received | The capture session expired, or the webhook endpoint is not registered. | Verify your webhook URL in the RiskOS™ dashboard. Check for Session Expired in case_notes_added events. |
SDK returns cameraPermissionDeclined | The consumer denied camera access. | Prompt the consumer to enable camera permissions in device settings. |
| Selfie Reverification returns an error | Missing or invalid previous_reference_id. | Verify that additional_context.previous_reference_id contains a valid UUID from a prior accepted DocV evaluation. |
| Capture App shows the wrong flow type | The use_case_key references a different flow. | Verify the use_case_key matches the intended flow in Settings > DocV App > Flow in the RiskOS™ Dashboard. |
Related resources
Updated 13 days ago
