Skip to main content

Overview

This flow extends the basic IDV flow by adding an OCR review screen after document capture. After the camera captures and processes the document, the extracted data is shown in a review form for the user to confirm or correct before verification is submitted. This is useful when you want users to verify OCR accuracy. Modules used: idv, form (WELCOME, CONSENT, REVIEW, LOADING, RESULT screens)
Instead of building from scratch, fork an existing flow from the public sample codes.

Flow Diagram

1

Welcome Screen

Initial greeting and introduction to the verification process.
2

Consent Screen

Capture user consent for data processing.
3

IDV Capture

Camera capture UI for document scanning. Document is automatically detected and captured.
4

Loading / Extraction

SDK extracts data from the captured document. A loading screen is shown.
5

Review Screen

Extracted data is displayed in a review form for user confirmation.
6

Verification Submitted

After review confirmation, verification is submitted automatically.
7

Result

Verification outcome is shown.
This flow requires a Google Places API key (googleApiKey) in the form provider configuration. The key powers the address autocomplete field in the review form. Without it, the address search will not work. Get a key from the Google Cloud Console with the Places API enabled.

Full Implementation

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>OneSDK IDV with Review</title>
    <script src="https://assets.frankiefinancial.io/one-sdk/v1/oneSdk.umd.js"></script>
    <style>
      body { margin: 0; font-family: sans-serif; background: #fff; }
      #onesdk-wrapper { position: relative; width: 100%; height: 100vh; }
      #onesdk-container { width: 100%; height: 100%; }
      #loading-overlay {
        position: absolute; top: 0; left: 0; width: 100%; height: 100%;
        z-index: 10; background: #fff;
      }
    </style>
  </head>
  <body>
    <div id="onesdk-wrapper">
      <div id="onesdk-container"></div>
      <div id="loading-overlay"></div>
    </div>

    <script>
      async function startOneSDK() {
        try {
          // 1. Generate session token (move to your backend in production)
          const tokenResponse = await fetch(
            "https://backend.kycaml.uat.frankiefinancial.io/auth/v2/machine-session",
            {
              method: "POST",
              headers: {
                authorization: "machine " + btoa("<CUSTOMER_ID>:<CUSTOMER_CHILD_ID>:<API_KEY>"),
                "Content-Type": "application/json",
              },
              body: JSON.stringify({
                permissions: {
                  preset: "one-sdk",
                  reference: "idv-review-" + Date.now(),
                },
              }),
            }
          );
          const session = await tokenResponse.json();

          // 2. Initialize OneSDK
          const oneSdk = await OneSDK({
            session: session,
            mode: "development",
            recipe: {
              form: {
                provider: {
                  name: "react",
                  googleApiKey: "<YOUR_GOOGLE_API_KEY>",
                },
              },
            },
          });

          const appContainer = "#onesdk-container";

          // 3. Create form and IDV components
          const welcome = oneSdk.component("form", {
            name: "WELCOME",
            type: "ocr",
          });

          const consent = oneSdk.component("form", { name: "CONSENT" });

          const idv = oneSdk.flow("idv");

          const review = oneSdk.component("form", {
            name: "REVIEW",
            type: "ocr",
          });

          const idvLoading = oneSdk.component("form", {
            name: "LOADING",
            title: { label: "Loading..." },
            descriptions: [{ label: "" }],
          });

          const extractLoading = oneSdk.component("form", {
            name: "LOADING",
            title: { label: "Extracting data..." },
            descriptions: [
              {
                label: "Hold tight, this can take up to 30 seconds. Please do not refresh this page or click the back button.",
              },
            ],
          });

          const submitLoading = oneSdk.component("form", {
            name: "LOADING",
            title: { label: "Submitting verification..." },
            descriptions: [
              {
                label: "Please do not refresh this page or click the back button.",
              },
            ],
          });

          const result = oneSdk.component("form", {
            name: "RESULT",
            type: "manual",
            state: "SUCCESS",
            title: { label: "Thanks, you're all done" },
            cta: null,
          });

          // 4. Wire up welcome → consent → IDV
          welcome.on("form:welcome:ready", () => {
            consent.mount(appContainer);
          });

          consent.on("form:consent:ready", () => {
            idvLoading.mount("#loading-overlay");
            idv.mount(appContainer);
          });

          // 5. Hide IDV loading once IDV component is ready
          let detectionStarted = false;

          idv.on("loading", (isLoading) => {
            if (!detectionStarted && !isLoading) {
              idvLoading.unmount();
            }
          });

          // 6. After document capture, show extraction loading
          idv.on("detection_complete", () => {
            detectionStarted = true;
            extractLoading.mount(appContainer);
          });

          // 7. Handle IDV completion — show review screen
          idv.on("results", ({ checkStatus }) => {
            if (checkStatus) {
              extractLoading.unmount();
              review.mount(appContainer);
            }
          });

          // 8. Handle review completion — submit and show result
          review.on("form:review:ready", async () => {
            submitLoading.mount(appContainer);
            try {
              await oneSdk.individual().submit({ verify: true });
              result.mount(appContainer);
            } catch (err) {
              console.error("Verification submission failed:", err);
              document.getElementById("onesdk-container").innerHTML =
                "<h2>Verification error</h2><p>Please try again.</p>";
            }
          });

          // 9. Handle errors
          idv.on("error", ({ message }) => {
            console.error("IDV error:", message);
            document.getElementById("onesdk-container").innerHTML =
              "<h2>Verification error</h2><p>Please try again.</p>";
          });

          // 10. Mount welcome screen to start
          welcome.mount(appContainer);
        } catch (error) {
          console.error("OneSDK initialization failed:", error);
        }
      }

      startOneSDK();
    </script>
  </body>
</html>

Step-by-Step Breakdown

1. Initialize the SDK

Pass the session object to OneSDK(). Include the recipe.form.provider configuration with a googleApiKey since this flow uses form components for the WELCOME, CONSENT, REVIEW, LOADING, and RESULT screens. The Google Places API key is required for the address autocomplete field in the review form.

2. Configure Components

Create WELCOME and CONSENT form components to collect user consent before starting capture. Create the IDV flow and form components for the REVIEW, LOADING, and RESULT screens.

3. Wire Up Events

Listen for events:
  • form:welcome:ready on welcome — user completed the welcome screen, mount consent
  • form:consent:ready on consent — user gave consent, mount the IDV flow
  • loading on IDV — show/hide loading screen while IDV component initializes (before capture)
  • detection_complete on IDV — document capture finished, show extraction loading screen
  • results on IDV — extraction complete, mount the review screen
  • form:review:ready on review — user confirmed data, submit verification

4. Handle Results

After the review form emits form:review:ready, mount a loading screen, then call sdk.individual().submit({ verify: true }) and mount the result screen. The loading screen prevents a blank screen during the async submission. See Error Scenarios for details.

Loading Screens

Three loading screens cover the async gaps in this flow:
Unmount clears the container. Calling .unmount() on a component removes all content from the element it was mounted to — not just the component itself. If you mount a LOADING screen and the IDV flow to the same container, unmounting the loading screen will also destroy the IDV content. Always mount loading screens to a separate element when they need to overlay another component.
IDV loading — shown while the IDV component initializes. The loading screen is mounted to a separate overlay div (#loading-overlay) that sits on top of the main container, so unmount() only clears the overlay without affecting the IDV content underneath:
<div id="onesdk-wrapper" style="position: relative;">
  <div id="onesdk-container"></div>
  <div id="loading-overlay"></div>
</div>
const idvLoading = oneSdk.component("form", {
  name: "LOADING",
  title: { label: "Loading..." },
  descriptions: [{ label: "" }],
});

// Mount loading to the overlay, IDV to the main container
idvLoading.mount("#loading-overlay");
idv.mount("#onesdk-container");

let detectionStarted = false;

idv.on("loading", (isLoading) => {
  if (!detectionStarted && !isLoading) {
    idvLoading.unmount(); // Safe — only clears #loading-overlay
  }
});
Extraction loading — shown after document capture while the SDK extracts data. Mount it on detection_complete and unmount when results fires:
const extractLoading = oneSdk.component("form", {
  name: "LOADING",
  title: { label: "Extracting data..." },
  descriptions: [
    {
      label: "Hold tight, this can take up to 30 seconds. Please do not refresh this page or click the back button.",
    },
  ],
});

idv.on("detection_complete", () => {
  detectionStarted = true;
  extractLoading.mount(appContainer);
});
Submission loading — shown after the user confirms the review while submit() completes. Mount it immediately in the form:review:ready handler before calling submit():
const submitLoading = oneSdk.component("form", {
  name: "LOADING",
  title: { label: "Submitting verification..." },
  descriptions: [
    {
      label: "Please do not refresh this page or click the back button.",
    },
  ],
});

Review Screen Configuration

The review screen displays extracted OCR data for user confirmation:
const review = oneSdk.component("form", {
  name: "REVIEW",
  type: "ocr",
});
Set type: "ocr" so the review form knows to display OCR-extracted fields. See Form Screens for customization options.

Submitting After Review

When the user confirms the review, the form:review:ready event fires. Mount a loading screen first so the user is not left with a blank screen during submission:
review.on("form:review:ready", async () => {
  submitLoading.mount(appContainer);
  try {
    await oneSdk.individual().submit({ verify: true });
    resultScreen.mount(appContainer);
  } catch (error) {
    console.error("Verification submission failed:", error);
  }
});

Customization Reference

AspectReference
SDK initialization optionsSDK Initialization
IDV flow configurationIDV Module
Form screen configurationForm Module
Screen names and typesForm Screens
Individual submissionIndividual Module
Event names and payloadsEvents
Error handling patternsError Scenarios