Skip to main content

Overview

The Document Upload flow allows users to submit identity documents by uploading files instead of capturing them with a camera. Users progress through three upload stages — required documents, partial documents (pick at least N from a group), and optional documents — then review their uploads before submission. This flow is useful for desktop-first applications, pre-scanned documents, or when camera access is unavailable. Modules used: form (WELCOME, CONSENT, REQUIRED_DOCUMENT_UPLOAD, PARTIAL_DOCUMENT_UPLOAD, OPTIONAL_DOCUMENT_UPLOAD, REVIEW, RESULT screens)
Instead of building from scratch, fork an existing flow from the public sample codes.
Entity must already exist. The document upload flow requires the entity used in the session to already exist — created via the API, the FrankieOne Portal, or a prior SDK flow. If you need to create the entity on the fly, use the Individual module or an eKYC form flow before starting the document upload.

Flow Diagram

1

Welcome Screen

Initial greeting and introduction (type doc_upload).
2

Consent Screen

Capture user consent for data processing.
3

Required Document Upload

User uploads all mandatory documents.
4

Partial Document Upload

User uploads at least the minimum required number from a group of documents.
5

Optional Document Upload

User can optionally upload additional supporting documents.
6

Review Screen

Uploaded documents displayed for confirmation.
7

Result

Upload success or failure displayed.

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 Document Upload</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-container { width: 100%; min-height: 100vh; }
    </style>
  </head>
  <body>
    <div id="onesdk-container"></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: "upload-" + Date.now(),
                },
              }),
            }
          );
          const session = await tokenResponse.json();

          // 2. Initialize OneSDK with docUpload recipe
          const oneSdk = await OneSDK({
            session: session,
            mode: "development",
            recipe: {
              form: {
                provider: { name: "react" },
                docUpload: {
                  requestedDocuments: [
                    { documentType: "PROOF_OF_ADDRESS" },
                    { documentType: "BANK_STATEMENT" },
                  ],
                  partialDocuments: [
                    {
                      title: "Additional Identity Documents",
                      minimumRequirement: 1,
                      documents: [
                        { documentType: "BIRTH_CERTIFICATE" },
                        { documentType: "UTILITY_BILL" },
                        { documentType: "TAX_RETURN" },
                      ],
                    },
                  ],
                  optionalDocuments: [
                    { documentType: "EMPLOYMENT_LETTER" },
                  ],
                },
              },
            },
          });

          const appContainer = document.querySelector("#onesdk-container");

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

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

          const requiredDocUpload = oneSdk.component("form", {
            name: "REQUIRED_DOCUMENT_UPLOAD",
          });

          const partialDocUpload = oneSdk.component("form", {
            name: "PARTIAL_DOCUMENT_UPLOAD",
          });

          const optionalDocUpload = oneSdk.component("form", {
            name: "OPTIONAL_DOCUMENT_UPLOAD",
          });

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

          const success = oneSdk.component("form", {
            name: "RESULT",
            state: "UPLOAD_SUCCESS",
          });

          const fail = oneSdk.component("form", {
            name: "RESULT",
            state: "UPLOAD_FAIL",
          });

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

          consent.on("form:consent:ready", () => {
            requiredDocUpload.mount(appContainer);
          });

          requiredDocUpload.on("form:required_document_upload:ready", () => {
            partialDocUpload.mount(appContainer);
          });

          partialDocUpload.on("form:partial_document_upload:ready", () => {
            optionalDocUpload.mount(appContainer);
          });

          optionalDocUpload.on("form:optional_document_upload:ready", () => {
            reviewDocUpload.mount(appContainer);
          });

          // 5. Handle review results
          reviewDocUpload.on("form:review:ready", () => {
            success.mount(appContainer);
          });

          reviewDocUpload.on("form:review:failed", () => {
            fail.mount(appContainer);
          });

          // 6. Mount the welcome screen to start the flow
          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 form.provider and form.docUpload recipe configuration. The docUpload property defines which documents appear at each upload stage.

2. Configure the docUpload Recipe

The docUpload recipe controls the three upload stages:
recipe: {
  form: {
    docUpload: {
      requestedDocuments: [
        { documentType: "PROOF_OF_ADDRESS" },
      ],
      partialDocuments: [
        {
          title: "Additional Identity Documents",
          minimumRequirement: 1,
          documents: [
            { documentType: "BIRTH_CERTIFICATE" },
            { documentType: "UTILITY_BILL" },
          ],
        },
      ],
      optionalDocuments: [
        { documentType: "EMPLOYMENT_LETTER" },
      ],
    },
  },
}

3. Create Upload Screen Components

Create a component for each upload stage using the dedicated screen names:
const requiredDocUpload = oneSdk.component("form", {
  name: "REQUIRED_DOCUMENT_UPLOAD",
});

const partialDocUpload = oneSdk.component("form", {
  name: "PARTIAL_DOCUMENT_UPLOAD",
});

const optionalDocUpload = oneSdk.component("form", {
  name: "OPTIONAL_DOCUMENT_UPLOAD",
});

4. Wire Up Events

Use .on(event) listeners to navigate through the upload stages. Each stage emits a form:<screen_name>:ready event when complete.

5. Handle Results

The review screen emits form:review:ready on success and form:review:failed on failure. Use UPLOAD_SUCCESS and UPLOAD_FAIL result states for the result screen. See Error Scenarios for details.

Upload Screen Types

The document upload flow uses three distinct screen types, each configured through the docUpload recipe:
ScreenRecipe PropertyBehavior
REQUIRED_DOCUMENT_UPLOADrequestedDocumentsAll listed documents must be uploaded
PARTIAL_DOCUMENT_UPLOADpartialDocumentsAt least minimumRequirement documents must be uploaded from each group
OPTIONAL_DOCUMENT_UPLOADoptionalDocumentsDocuments may be uploaded but are not required

Required vs Optional Documents

Configure the three tiers in the docUpload recipe:
docUpload: {
  // All of these must be uploaded
  requestedDocuments: [
    { documentType: "PROOF_OF_ADDRESS" },
    { documentType: "BANK_STATEMENT" },
  ],
  // At least minimumRequirement from each group
  partialDocuments: [
    {
      title: "Additional Identity Documents",
      minimumRequirement: 1,
      documents: [
        { documentType: "BIRTH_CERTIFICATE" },
        { documentType: "UTILITY_BILL" },
        { documentType: "TAX_RETURN" },
      ],
    },
  ],
  // Completely optional
  optionalDocuments: [
    { documentType: "EMPLOYMENT_LETTER" },
  ],
}
See Document Upload Configuration for the full type reference and validation rules.

Review After Upload

The review screen shows all uploaded documents for confirmation. Use type: "doc_upload" to render the upload-specific review UI:
const reviewDocUpload = oneSdk.component("form", {
  name: "REVIEW",
  type: "doc_upload",
});

reviewDocUpload.on("form:review:ready", () => {
  success.mount(appContainer);
});

reviewDocUpload.on("form:review:failed", () => {
  fail.mount(appContainer);
});
Use the UPLOAD_SUCCESS and UPLOAD_FAIL result states for the outcome screens:
const success = oneSdk.component("form", {
  name: "RESULT",
  state: "UPLOAD_SUCCESS",
});

const fail = oneSdk.component("form", {
  name: "RESULT",
  state: "UPLOAD_FAIL",
});

Customization Reference

AspectReference
SDK initialization optionsSDK Initialization
Form screen configurationForm Module
Document upload recipeDocument Upload Configuration
Screen names and typesForm Screens
Custom field configurationForm Configuration
Event names and payloadsEvents
Error handling patternsError Scenarios