Receive DocV Results

Receive Predictive DocV results two ways — subscribe to webhooks for real-time delivery, or poll the evaluation with a GET request.

DocV is asynchronous: the consumer completes capture on their own device with no further backend interaction, so the original API response can't carry the outcome. Once capture finishes, RiskOS™ resumes the paused evaluation and produces a decision. You receive that result in one of two ways.


Two ways to receive results

MethodHow it worksUse it when
Webhooks (recommended)RiskOS™ pushes the result to your endpoint in real time as the evaluation completes.You can host a public endpoint and want results delivered automatically.
GET request (polling)Your backend requests the evaluation's current state on demand.You can't host a webhook endpoint, or you need to reconcile a missed webhook or confirm the decision before acting.

Both methods return the same result schema. Webhooks are the recommended path; use a GET request as a fallback or to reconcile state. The Decision handling steps that follow apply no matter which method you use.


Method 1: Webhooks (recommended)

DocV delivers results through two webhook events. Subscribe to both for full visibility, then act on the final decision.


Subscribe to webhooks

Configure your webhook endpoint and event subscriptions in the RiskOS™ Dashboard before you run an evaluation. Subscribe to two events:

  • evaluation_completed — fires when the evaluation finishes; carries the final decision and DocV results. Subscribe to this if you only need the outcome for decision-making.
  • case_notes_added — fires as the consumer moves through capture; carries lightweight progress notes. Subscribe to this as well if you want progress visibility.

To subscribe:

  1. In the RiskOS™ Dashboard, go to Developer Workbench > Webhooks.
  2. Create (or edit) a webhook and subscribe to the case_notes_added and evaluation_completed event types.
  3. Click Continue to Test, then Save to begin receiving real-time notifications.

For endpoint configuration, retries, and signature verification, see Configure Webhooks in RiskOS™. For the exhaustive event and payload schemas, see the API Reference.


Workflow completion

The paused evaluation resumes only on a terminal capture event. Intermediate progress events leave the workflow paused.

Capture outcomeResumes workflow?Result
Session CompleteYesCapture finished; the workflow continues and produces a decision.
Session ExpiredYesLink expired; the workflow resumes with a terminal-negative outcome.
Consent DeclinedYesConsumer declined consent; the workflow resumes with a terminal-negative outcome.
Progress events (app opened, images uploaded)NoIndicators only; the workflow stays paused.

Capture progress: case_notes_added

These lightweight events track where the consumer is in the capture flow. They do not contain PII.

data.notes valueWhen it fires
Process InitiatedDocV is initiated; waiting for the consumer to open the link or scan the QR code.
Capture App OpenedConsumer opened the Capture App.
Document Front UploadedFront of the document uploaded.
Document Back UploadedBack of the document uploaded.
Document Selfie UploadedSelfie uploaded.
Documents Upload SuccessfulAll required images uploaded.
Session CompleteCapture and upload completed.
Consent DeclinedConsumer declined consent (terminal).
Session ExpiredCapture link expired (terminal). By default, the link expires 24 hours after it is created. See Customize the Capture App to change this setting.
📘

Filter to DocV-generated notes:

Both DocV webhooks and manual reviewers can create case notes. To process only DocV-generated notes, filter for event_type == "case_notes_added" and data.reviewer_id == "Webhook".

Process Initiated

  {
    "id": "9fb17966-c07d-4e33-80d0-6f0a08907c3a",
    "origId": "45ac9531-60ae-4bc7-805e-f7823e4e5545",
    "eventGroup": "DocvNotification",
    "reason": "WAITING_FOR_USER_TO_REDIRECT",
    "environmentName": "Production",
    "event": {
      "created": "2024-08-07T21:13:10.406Z",
      "customerUserId": "111-222-333",
      "docVTransactionToken": "45ac9531-60ae-4bc7-805e-f7823e4e5545",
      "eventType": "WAITING_FOR_USER_TO_REDIRECT",
      "message": "Process Initiated",
      "referenceId": "45ac9531-60ae-4bc7-805e-f7823e4e5545",
      "userId": "444-555-666"
    }
  }

App Opened

{
  "data": {
    "attachments": null,
    "environment_name": "Sandbox",
    "eval_id": "6c81b2ee-4f7e-4ea1-9696-160ecb5b341a",
    "id": "onb-12345",
    "notes": "Capture App Opened",
    "reviewer_id": "Webhook",
    "updated_at": "2025-07-07T21:01:31.29234384Z",
    "workflow": "individual_onboarding"
  },
  "event_at": "2025-07-07T21:01:31.296383102Z",
  "event_id": "6414942b-b3ac-4bec-ad00-da6ea23380d3",
  "event_type": "case_notes_added"
}

Image Upload Events

{
  "data": {
    "attachments": null,
    "environment_name": "Sandbox",
    "eval_id": "6c81b2ee-4f7e-4ea1-9696-160ecb5b341a",
    "id": "onb-12345",
    "notes": "Document Front Uploaded",
    "reviewer_id": "Webhook",
    "updated_at": "2025-07-07T21:07:32.584206875Z",
    "workflow": "individual_onboarding"
  },
  "event_at": "2025-07-07T21:07:32.589211579Z",
  "event_id": "903584c5-8771-4759-b23e-9fc9d9b80b11",
  "event_type": "case_notes_added"
}

Capture Session Outcomes

{
  "data": {
    "attachments": null,
    "environment_name": "Sandbox",
    "eval_id": "6c81b2ee-4f7e-4ea1-9696-160ecb5b341a",
    "id": "onb-12345",
    "notes": "Documents Upload Successful",
    "reviewer_id": "Webhook",
    "updated_at": "2025-07-07T21:08:02.130094221Z",
    "workflow": "individual_onboarding"
  },
  "event_at": "2025-07-07T21:08:02.133677529Z",
  "event_id": "d70203eb-9d14-4a16-ad5b-fc45e6be7ed5",
  "event_type": "case_notes_added"
}
📘

Terminal note classification:

Treat Consent Declined and Session Expired as terminal-negative, and Session Complete and Documents Upload Successful as terminal-positive.

The table and tabs earlier in this topic list every DocV case_notes_added note and its payload. The webhook reference defines the generic case_notes_added event shape, but this page is the only place that documents the DocV-specific data.notes values — treat it as the source of truth for them.


Automate terminal handling

You can configure your workflow to act on a terminal capture outcome automatically, without manual review. Add a Condition step or Decision Rules step after the Document Request enrichment:

  • Auto-cancel — check for a Session Expired or Consent Declined outcome, then route to a Decision step configured with a Cancel outcome.
  • Auto-resubmit — check for a resubmit enrichment decision, then route to a Decision step configured with a Resubmit outcome to prompt the consumer to recapture without a manual review.

When a session expires, RiskOS™ can generate a new docvTransactionToken within the same evaluation — configure a re-attempt on the Session Expired event to preserve the original evaluation context and avoid re-running upstream enrichments.


Final decision: evaluation_completed

This event fires when the evaluation finishes. The top-level data.decision (uppercase ACCEPT, REVIEW, REJECT) is the workflow decision. The DocV module decision and extracted data are nested inside data.data_enrichments.

The enrichment object name varies by flow type (documentVerification, secondaryDocument, selfieIntelligence, selfieReverification). For each flow's full enrichment payload and extracted-data schema, see the Enrichment API Reference in the RiskOS™ Dashboard.

A complete evaluation_completed payload includes workflow metadata, the SocureDocRequest enrichment, and the Socure Document Verification enrichment with extracted documentData and the digitalIntelligence.device block.

👍

Correlation tips:

  • Persist eval_id alongside your internal transaction ID.
  • From enrichment responses, persist values like referenceId or docvTransactionToken when present.
{
  "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",
    "workflow_id": "5937a624-f298-452c-9169-ceeae9e66b74",
    "workflow_version": "1.0.0",
    "environment_name": "Sandbox",
    "eval_source": "API",
    "eval_start_time": "2025-07-17T01:18:27Z",
    "eval_end_time": "2025-07-17T01:20:01Z",
    "eval_status": "evaluation_completed",
    "decision": "ACCEPT",
    "decision_at": "2025-07-17T01:20:01Z",
    "status": "CLOSED",
    "sub_status": "Accept",
    "tags": [],
    "notes": "",
    "review_queues": [
      "Default Queue"
    ],
    "data_enrichments": [
      {
        "enrichment_name": "Socure Document Request - Default Flow",
        "enrichment_endpoint": "https://service.socure.com/api/5.0/documents/request",
        "enrichment_provider": "SocureDocRequest",
        "status_code": 200,
        "response": {
          "referenceId": "ed6a5077-b272-4a75-8c21-b284e10927cd",
          "status": "SESSION_COMPLETE",
          "data": {
            "docvTransactionToken": "7d6ad42b-f804-4255-b25e-268b8a77c86f",
            "url": "https://verify.socure.com/#/dv/7d6ad42b-f804-4255-b25e-268b8a77c86f"
          }
        }
      },
      {
        "enrichment_name": "Socure Document Verification",
        "enrichment_endpoint": "https://service.socure.com/api/5.0/documents/verify",
        "enrichment_provider": "Socure",
        "status_code": 200,
        "response": {
          "referenceId": "ed6a5077-b272-4a75-8c21-b284e10927c",
          "documentVerification": {
            "decision": {
              "name": "standard",
              "value": "accept"
            },
            "reasonCodes": [
              "I831",
              "I836"
            ],
            "documentType": {
              "type": "Drivers License",
              "country": "USA",
              "state": "NY"
            },
            "documentData": {
              "firstName": "Test",
              "surName": "User",
              "fullName": "Test User",
              "dob": "1990-01-01",
              "documentNumber": "TST1234567",
              "expirationDate": "2030-01-01"
            },
            "digitalIntelligence": {
              "device": {
                "id": "e92ee549-e3c7-4307-9be7-32fefb0db9a4",
                "globalDeviceId": "aecddd42-f223-3333-940c-87baab4c6645",
                "sessionCreatedAt": "2025-07-17T01:18:28Z",
                "deviceCaptureAt": "2025-07-17T01:18:45Z",
                "computed": {
                  "statisticalId": "b90c8faddcd15cd5eafc09bfe4e460d557c5e516f8c5b44b8d3ec1e6f60c30f8",
                  "isVirtualMachine": false,
                  "sessionAgeMinutes": 45,
                  "deviceNetworkTimezoneOffsetDiffMinutes": 60
                },
                "network": {
                  "connectionIp": "203.0.113.10",
                  "webRtcPublicIp": "203.0.113.10",
                  "webRtcInternalIp": "10.0.0.15",
                  "realIp": "203.0.113.10",
                  "isTor": false,
                  "isProxy": false,
                  "isVpn": false,
                  "isConsumerPrivacy": false,
                  "isRiskyNetwork": false,
                  "isp": "Example ISP",
                  "ispType": "home",
                  "asn": 64500,
                  "asnName": "EXAMPLE-NET",
                  "domainName": "example.net",
                  "org": "Example Org",
                  "isMobileCarrier": false,
                  "speed": "broadband",
                  "networkLocation": {
                    "countryCode": "US",
                    "region": "NY",
                    "city": "New York",
                    "postalCode": "10001",
                    "latitude": 40.75,
                    "longitude": -73.99,
                    "metroCode": 501,
                    "continentCode": "NA",
                    "timezoneName": "America/New_York",
                    "gmtOffset": "-0500"
                  }
                }
              }
            }
          }
        }
      }
    ]
  }
}

Method 2: Poll with a GET request

When you can't host a webhook endpoint — or you need to reconcile a missed webhook or confirm the final decision before acting — poll for an evaluation's current state with a GET request.

Call the Retrieve Evaluation Data endpoint with the eval_id you stored when you created the evaluation:

GET https://riskos.sandbox.socure.com/api/evaluation/{eval_id}
GET https://riskos.socure.com/api/evaluation/{eval_id}
curl --request GET \
  --url https://riskos.sandbox.socure.com/api/evaluation/YOUR_EVAL_ID \
  --header "Authorization: Bearer YOUR_API_KEY"
ParameterInRequiredDescription
eval_idPathRequiredThe system-generated evaluation ID from the initial POST /api/evaluation response.
include_inputQueryOptionalSet to true to include the original and transformed input data under the input field. Defaults to false.

The response uses the same schema as the evaluation_completed webhook. While capture is still in progress, the evaluation is paused — status is ON_HOLD, eval_status is evaluation_paused, and decision is REVIEW:

{
  "id": "client-transaction-12345",
  "eval_id": "11111111-2222-3333-4444-555555555555",
  "workflow": "consumer_onboarding",
  "decision": "REVIEW",
  "status": "ON_HOLD",
  "eval_status": "evaluation_paused"
}

Once capture reaches a terminal outcome, the same call returns the final decision (ACCEPT or REJECT) and the full data_enrichments payload shown in the evaluation_completed example.

📘

Poll responsibly:

Treat the GET endpoint as a fallback, not a substitute for webhooks. Poll on a reasonable interval with exponential backoff, stop once eval_status is evaluation_completed, and honor the Retry-After header if you receive a 429 response.


Decision handling

Whether the result arrives by webhook or a GET response, match the eval_id from the payload to the evaluation you stored when you created it, then route the consumer on the top-level decision.

DecisionWhat it meansWhat to do
ACCEPTThe consumer passed verification.Grant access or proceed with onboarding.
REJECTThe consumer failed verification.Deny access or escalate for manual review.
REVIEWThe evaluation needs manual review.Route to your review queue. Do not treat as a final pass/fail.
ON_HOLDThe workflow is still paused (capture not yet complete).Wait for a terminal capture event; no action yet.
function handleEvaluationWebhook(payload: EvaluationCompletedEvent) {
  const { eval_id, decision } = payload.data;
  const evaluation = lookupEvaluation(eval_id); // correlate with stored eval

  if (decision === "ACCEPT") completeOnboarding(evaluation.userId);
  if (decision === "REJECT") denyAccess(evaluation.userId);
}
⚠️

Route only on the workflow decision:

Use the top-level data.decision for routing. The nested DocV module decision (documentVerification.decision.value) is a recommendation that feeds the workflow — don't branch your application on it directly.


DocV module decision

The nested module decision describes the document verification outcome that the workflow used to reach its decision. It's useful for resubmission UX and review tooling:

decision.valueMeaningTypical handling
acceptImages met validation criteria and were verified.Workflow proceeds to ACCEPT.
rejectImages failed required validation criteria.Workflow resolves to REJECT.
resubmitImage quality or missing data requires recapture.Document Verification runs once per evaluation, so a resubmit resolves the workflow to REJECT. To let the consumer try again, create a new evaluation.
reviewImages did not meet configured criteria and need manual review. Returned only if enabled in your DocV settings.Route to your review queue.
📘

No in-session retry:

Document Verification runs once per evaluation. A resubmit or reject module decision, an expired session, declined consent, or a request/response error all resolve the evaluation to REJECT. To give the consumer another attempt, create a new evaluation.



Payload reference

The DocV webhook payload fields appear in a dedicated reference: DocV Webhook Payload Reference. It covers the data_enrichments array, the documentVerification, documentType, decision, and documentData objects, and the Socure Pass enrichment results.


Best practices

  • Correlate every result to a stored evaluation using eval_id, whether it arrives by webhook or GET response.
  • Prefer webhooks for delivery; use a GET request as a fallback, and poll with exponential backoff rather than on a tight loop.
  • Route only on the terminal workflow decision (ACCEPT or REJECT); never on the initial REVIEW.
  • Treat case_notes_added events as progress indicators, not decision signals.
  • Interpret a mismatch from the reasonCodes array, and resolve codes at runtime against the reason code catalog rather than hard-coding individual values.
  • Tune how mismatches are scored in your decision logic configuration, not in application code.
  • Protect the result payload — it can contain PII.
  • For the exhaustive payload schemas, see the API Reference.

FAQs

What does an OCR or data mismatch mean?

DocV reads the identity fields printed on the document — name, date of birth, document number, address, and issue and expiration dates — using OCR, the barcode, and the machine-readable zone (MRZ), and returns them in the documentData object. It then checks those values for consistency at up to three levels. A discrepancy at any level is a mismatch, and it's one of the most common reasons a document resolves to resubmit, review, or reject instead of accept.

CheckWhat it comparesA mismatch usually means
Cross-sourceOCR text vs the barcode vs the MRZ on the same document.The image is low quality, or the document has been altered so the printed text no longer matches the encoded data.
Input correlationThe extracted documentData vs the PII you submitted in the evaluation request (for example, given_name, family_name, date_of_birth).The document doesn't belong to the person you're onboarding — or the data you submitted contains a typo.
Source of Truth (optional add-on)The extracted PII vs authoritative records such as state DMV databases.The identity doesn't exist in official records, or a field was modified. See Source of Truth Checks.
How do I tell why a document was rejected or sent to resubmit?

Two fields in the documentVerification object tell you what happened and why:

  • decision.value — the severity of the outcome: accept, review, resubmit, or reject.
  • reasonCodes — machine-readable flags that name the specific discrepancy (data mismatch, capture quality, authenticity). Look each code up in the reason code catalog and resolve codes at runtime rather than hard-coding individual values.

If DocV can't read enough of the document to extract its fields — usually blur, glare, or a cropped image — the documentData object is absent and decision.value is resubmit. Treat an absent documentData as a capture-quality problem, not a data mismatch.

What should I do when a document fails for a data mismatch?

Route your application on the workflow decision (see Decision handling), and use the module decision.value to shape the consumer's next step and your review tooling:

decision.valueWhat it means for a mismatchWhat to do
acceptNo material discrepancy, or differences fell within your configured tolerance.Proceed with onboarding.
reviewData-consistency reason codes need a human to adjudicate. Returned only if review is enabled in your DocV Product Settings.Route to your review queue with the reasonCodes attached.
resubmitImage quality or missing data prevented a reliable read, or input correlation failed in a recoverable way.Create a new evaluation and prompt the consumer to recapture. Tailor the prompt from the reasonCodes. If the mismatch is against your submitted PII, also confirm the data you sent is correct.
rejectThe document failed required data-match validation.Deny per your policy, or escalate to manual review if your risk strategy allows a second look.

Which discrepancies drive review, resubmit, or reject depend on your DocV product settings and workflow logic — not fixed in the SDK. To change how mismatches are scored, adjust your decision logic configuration instead of branching on individual reason codes.

How do I test OCR and data mismatch outcomes in Sandbox?

Force a mismatch outcome with a Sandbox transaction token, then confirm your application handles it:

Transaction tokenScenario
SOC12031182Reject — Material/Data Match
SOC17259150Resubmit — Input Correlation

For the full token list and setup, see DocV Sandbox Testing.

What happens if the consumer abandons capture and the link expires?

When the link validity window passes, you receive a case_notes_added webhook with Session Expired, which resumes the paused workflow. You have three options:

  • Re-attempt within the same evaluation: Configure your workflow to trigger a re-attempt after Session Expired. RiskOS™ generates a new docvTransactionToken without re-running the full workflow or creating a new evaluation.
  • Create a new evaluation: Generate a fresh docvTransactionToken and link. To tie attempts back to the same business record, include your own external transaction ID in the id field.
  • Cancel automatically: Configure your workflow to cancel the evaluation on Session Expired instead of re-attempting.

Related