> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frankieone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Document Upload

> File-based document collection using upload screens — supports required, partial, and optional document types with review.

## 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`](/docs/sdk-reference/form-module) (WELCOME, CONSENT, REQUIRED\_DOCUMENT\_UPLOAD, PARTIAL\_DOCUMENT\_UPLOAD, OPTIONAL\_DOCUMENT\_UPLOAD, REVIEW, RESULT screens)

<Callout icon="star" color="#3DD892" iconType="regular">
  Instead of building from scratch, fork an existing flow from the [public sample codes](https://github.com/FrankieOne/frontend-onesdk-public-sample-codes/).
</Callout>

<Callout icon="bell" color="#FFCA16" iconType="regular">
  **Entity must already exist.** The document upload flow requires the entity used in the session to already exist — created via the [API](/docs/reference), the FrankieOne Portal, or a prior SDK flow. If you need to create the entity on the fly, use the [Individual module](/docs/sdk-reference/individual-module) or an [eKYC form flow](/docs/embedded-flows/ekyc) before starting the document upload.
</Callout>

## Flow Diagram

<Steps>
  <Step title="Welcome Screen">
    Initial greeting and introduction (type `doc_upload`).
  </Step>

  <Step title="Consent Screen">
    Capture user consent for data processing.
  </Step>

  <Step title="Required Document Upload">
    User uploads all mandatory documents.
  </Step>

  <Step title="Partial Document Upload">
    User uploads at least the minimum required number from a group of documents.
  </Step>

  <Step title="Optional Document Upload">
    User can optionally upload additional supporting documents.
  </Step>

  <Step title="Review Screen">
    Uploaded documents displayed for confirmation.
  </Step>

  <Step title="Result">
    Upload success or failure displayed.
  </Step>
</Steps>

## Full Implementation

<Tabs>
  <Tab title="Vanilla HTML/JS">
    ```html theme={null}
    <!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>
    ```
  </Tab>

  <Tab title="React">
    ```jsx theme={null}
    import { useEffect, useRef } from "react";
    import OneSdk from "@frankieone/one-sdk";

    export default function DocUploadFlow({ session }) {
      const initRef = useRef(false);

      useEffect(() => {
        if (!session || initRef.current) return;
        initRef.current = true;

        (async () => {
          try {
            const oneSdk = await OneSdk({
              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");

            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",
            });

            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);
            });

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

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

            welcome.mount(appContainer);
          } catch (error) {
            console.error("OneSDK initialization failed:", error);
          }
        })();
      }, [session]);

      return <div id="onesdk-container" style={{ width: "100%", minHeight: "100vh" }} />;
    }
    ```
  </Tab>
</Tabs>

## Step-by-Step Breakdown

### 1. Initialize the SDK

Pass the session object to [`OneSDK()`](/docs/sdk-reference/sdk-initialization). 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](/docs/sdk-reference/form-module/configuration#document-upload-configuration) controls the three upload stages:

```javascript theme={null}
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:

```javascript theme={null}
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)`](/docs/sdk-reference/events) 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](/docs/sdk-reference/form-module/configuration) for the result screen. See [Error Scenarios](/docs/sdk-reference/error-scenarios) for details.

## Upload Screen Types

The document upload flow uses three distinct screen types, each configured through the [`docUpload` recipe](/docs/sdk-reference/form-module/configuration#document-upload-configuration):

| Screen                     | Recipe Property      | Behavior                                                                 |
| -------------------------- | -------------------- | ------------------------------------------------------------------------ |
| `REQUIRED_DOCUMENT_UPLOAD` | `requestedDocuments` | All listed documents **must** be uploaded                                |
| `PARTIAL_DOCUMENT_UPLOAD`  | `partialDocuments`   | At least `minimumRequirement` documents must be uploaded from each group |
| `OPTIONAL_DOCUMENT_UPLOAD` | `optionalDocuments`  | Documents **may** be uploaded but are not required                       |

## Required vs Optional Documents

Configure the three tiers in the `docUpload` recipe:

```javascript theme={null}
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](/docs/sdk-reference/form-module/configuration#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:

```javascript theme={null}
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:

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

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

## Customization Reference

| Aspect                     | Reference                                                                                                    |
| -------------------------- | ------------------------------------------------------------------------------------------------------------ |
| SDK initialization options | [SDK Initialization](/docs/sdk-reference/sdk-initialization)                                                 |
| Form screen configuration  | [Form Module](/docs/sdk-reference/form-module)                                                               |
| Document upload recipe     | [Document Upload Configuration](/docs/sdk-reference/form-module/configuration#document-upload-configuration) |
| Screen names and types     | [Form Screens](/docs/sdk-reference/form-module/screens)                                                      |
| Custom field configuration | [Form Configuration](/docs/sdk-reference/form-module/configuration)                                          |
| Event names and payloads   | [Events](/docs/sdk-reference/events)                                                                         |
| Error handling patterns    | [Error Scenarios](/docs/sdk-reference/error-scenarios)                                                       |
