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

# ISV Onboarding API

# Overview

This API set is designed for **ISV (Independent Software Vendor)** customers: Party A initiates onboarding applications for its end customer (companies), and the OristaPay platform handles the full process including company search, compliance review, IDV, signing, and automatic wallet creation.

After completing onboarding, ISV customers receive `companyCode` and `walletId`, and can directly use the [ISV Transaction API](./transaction-api) to perform transfers, payment collection, Payout, and other transactions **on behalf of merchants**. **Transaction API fields remain unchanged** — Party A simply passes `walletId` as normal, and OristaPay automatically enforces authorization based on the ISV ↔ merchant relationship.

<Note>
  The authentication, signing, request headers, response envelope, file upload, and error code system of this API set are **fully consistent with the RDPAY API**. For details not repeated in this document, please refer to the [ISV Transaction API](./transaction-api).
</Note>

## Business Flow

```mermaid theme={null}
sequenceDiagram
    participant A as ISV (A)
        participant Platform as OristaPay
        participant B as Merchant (B)

    rect rgb(242,247,250)
                note over A,Platform: Submit Application
                A->>+Platform: File Upload (FileUpload)
                Platform-->>-A: fileKey
                A->>+Platform: Submit Application (SubmitApplication)
                Platform->>Platform: Validate profile
                Platform-->>-A: Return applicationNo (sync)
    end

    rect rgb(235,245,255)
                note over A,Platform: Background Async Review
                Platform->>Platform: Company search
                Platform->>+A: webhook ONBOARDING_APPLICATION_STATUS_NOTIFICATION (PENDING_USER)
                A-->>-Platform: 200 OK
    end

    rect rgb(227,242,253)
                note over A,B: IDV + Signing
                A->>+Platform: Get IDV Link (GetIdvLink)
                Platform-->>-A: linkUrl
                A->>+B: Forward H5 link (email/SMS/QR)
                B->>+Platform: Open H5 link, complete IDV → signing
                Note over Platform,A: Success events do not push webhooks; Party A polls via QueryApplication
                opt IDV failed (vendor failure / manual case failure)
                    Platform->>+A: webhook ONBOARDING_KEY_PEOPLE_IDV_FAILED
                    A-->>-Platform: 200 OK
        end
    end

    rect rgb(220,237,200)
                note over A,Platform: Final State
                Platform->>Platform: All review nodes pass + auto-create wallet
                Platform->>+A: webhook ONBOARDING_APPLICATION_STATUS_NOTIFICATION (APPROVED)
                A-->>-Platform: 200 OK
                A->>+Platform: Query Full Company Profile (QueryMainCompanyProfile)
                Platform-->>-A: Company details + walletId
    end
```

## Identifier Conventions

| Identifier         | Generated By                        | Purpose                                                                                         |
| ------------------ | ----------------------------------- | ----------------------------------------------------------------------------------------------- |
| `extApplicationNo` | Party A                             | External application number, globally unique                                                    |
| `applicationNo`    | OristaPay                           | OristaPay application number; primary key for queries and callback payloads                     |
| `companyCode`      | OristaPay (generated on approval)   | Company code; used for querying full profile and as company identity in subsequent transactions |
| `peopleId`         | OristaPay (generated on submission) | keyPeople global ID; used for obtaining IDV/signing links                                       |
| `walletId`         | OristaPay (after wallet creation)   | Wallet ID; used by subsequent transaction APIs                                                  |

# OnboardingAPI

## API Index

| # | Path                                          | Purpose                                                     |
| - | --------------------------------------------- | ----------------------------------------------------------- |
| 1 | `POST /storage/media/dg-putObject?mediaType=` | File upload                                                 |
| 2 | `POST /api/v1/onboarding/application/submit`  | Submit onboarding application                               |
| 3 | `POST /api/v1/onboarding/application/query`   | Query application progress                                  |
| 4 | `POST /api/v1/onboarding/idv/link`            | Get IDV link (**single link completes both IDV + signing**) |
| 5 | `POST /api/v1/onboarding/link/refresh`        | Refresh link expiration                                     |
| 6 | `POST /api/v1/onboarding/profile`             | Query approved full company profile                         |

***

## 1. File Upload (fileUpload)

**Interface Overview**

Before submitting underlying customer information, you need to use this interface to upload files related to the company.

**Request Parameters**

| **Field Name** | **Type** | **M / O / CM** | **Description**                      |
| :------------- | :------- | :------------- | :----------------------------------- |
| mediaType      | string   | M              | Enum: jpeg / jpg / png / pdf / gif   |
| mediaFile      | file     | M              | The uploaded file, not exceeding 20M |

**Response Parameters**

| **Field Name** | **Type** | **Description**                                                                                                                                 |
| :------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------- |
| code           | int32    | Response code, indicating the result of request processing. For example, 1 indicates success, other values indicate different error situations. |
| message        | string   | Response message, providing detailed processing information or error explanation.                                                               |
| data           | string   | Returned when validation is successful, the returned data is the key of the file.                                                               |

**Response Example**

```json theme={null}
{
	"code":1,
	"message":"success",
	"data":"123876789124.png"
}
```

## 2. Submit Application (SubmitApplication)

**Interface Overview**

Submit a company onboarding application for a downstream merchant (Party B). Use this endpoint for first-time submissions or resubmissions after a previous application was rejected (`REJECTED`).

**Request Parameters**

| Field Name       | Type        | M / O / CM | Description                                                                           |
| :--------------- | :---------- | :--------- | :------------------------------------------------------------------------------------ |
| extApplicationNo | string(128) | M          | External application number generated by Party A, globally unique                     |
| profile          | Object      | M          | Company profile form. See [Company Profile Form](#company-profile-form) for structure |

**Response Parameters**

| Field Name | Type            | Description                                                                            |
| :--------- | :-------------- | :------------------------------------------------------------------------------------- |
| code       | int32           | Response code: `1` indicates success; other values indicate different error conditions |
| message    | string          | Response message                                                                       |
| data       | ApplicationData | Returned when application is accepted successfully                                     |
| errors     | Array\<Errors>  | Returned when profile validation fails (`code = 6802`)                                 |

**ApplicationData Field Description**

| Field Name            | Type   | Description                                                                             |
| :-------------------- | :----- | :-------------------------------------------------------------------------------------- |
| applicationNo         | string | OristaPay application number; a new number is generated on resubmission after rejection |
| applicationCreateTime | int64  | Application creation time, millisecond timestamp                                        |

**Errors Field Description**

| Field Name | Type   | Description                                                                |
| :--------- | :----- | :------------------------------------------------------------------------- |
| message    | string | See [Profile Error Descriptions](#3-profile-error-descriptions-code--6802) |

**Request Example**

```json theme={null}
{
  "extApplicationNo": "EXT20260512001",
  "profile": {
    "entityDetail": { "businessType": 1, "incorpPlace": "HKG" },
    "businessDetail": { "list": [{ "subIndustryCode": "I300734" }] },
    "shareholder": { "list": [{ "shareholderId": 1 }] },
    "keyPeople": { "quorum": 2, "directorNum": 2, "people": [] },
    "accountOpeningQuestionnaire": {
      "accountOpeningPurposes": ["PAYMENT_FOR_HK_BUSINESS"],
      "expectedMonthlyVolume": "FROM_2_5M_TO_5M_HKD",
      "handleClientMoney": false
    }
  }
}
```

**Response Example (Success)**

```json theme={null}
{
  "code": 1,
  "message": "Success",
  "data": {
    "applicationNo": "A202605258664",
    "applicationCreateTime": 1747094400000
  }
}
```

**Response Example (Profile Validation Failed)**

```json theme={null}
{
  "code": 6802,
  "message": "Profile validation failed.",
  "errors": [
    { "message": "[Business details]Industry code cannot be empty!" },
    { "message": "[Entity details]Please upload a valid proof of Business Registration" }
  ]
}
```

**Key Constraints**

* **Idempotency key `(extApplicationNo)`**: Repeated submissions with the same `extApplicationNo` and same body → returns the original `applicationNo`; different body → `6801`
* **Merchant concurrency guard**: Only one **active** application is allowed per Party B `(CI number / BR number)` under the same Party A. A new application can only be submitted after the current one reaches a final state (`APPROVED` / `REJECTED`)
* **Resubmission after rejection**: Must use a **new** `extApplicationNo`; a new `applicationNo` is generated on submission
* **Company search failure = automatic rejection**: In the API path, whether it's a **company search classification failure** (CI/BR not found, etc.) or a **search comparison mismatch** (data inconsistency), the application is directly set to `REJECTED` and a `REJECTED` webhook is pushed. Party A must use a new `extApplicationNo` to resubmit

## 3. Query Application (QueryApplication)

**Interface Overview**

Query application progress by `applicationNo` or `extApplicationNo` (choose one).

**Request Parameters**

| Field Name       | Type   | M / O / CM | Description                  |
| :--------------- | :----- | :--------- | :--------------------------- |
| applicationNo    | string | CM         | OristaPay application number |
| extApplicationNo | string | CM         | Party A application number   |

**Response Parameters**

| Field Name | Type                 | Description              |
| :--------- | :------------------- | :----------------------- |
| code       | int32                | Response code            |
| message    | string               | Response message         |
| data       | QueryApplicationData | Returned when `code = 1` |

**QueryApplicationData Field Description**

| Field Name            | Type   | Description                                                              |
| :-------------------- | :----- | :----------------------------------------------------------------------- |
| applicationNo         | string | OristaPay application number                                             |
| extApplicationNo      | string | Party A application number                                               |
| applicationCreateTime | int64  | Application creation time, millisecond timestamp                         |
| applicationStatus     | string | Review status: `UNDER_REVIEW` / `PENDING_USER` / `APPROVED` / `REJECTED` |
| companyCode           | string | Company code (returned after approval)                                   |
| walletId              | int64  | Wallet ID (returned after wallet creation)                               |
| profile               | Object | Company profile form. See [Company Profile Form](#company-profile-form)  |

<Note>
  **`applicationStatus` values**:

  * `UNDER_REVIEW`: Under acceptance or review (covers all non-final, non-pending-user stages including company search, manual review, etc.)
  * `PENDING_USER`: Company search passed; waiting for keyPeople to complete IDV/signing. **Only now (and only now)** can `GetIdvLink` be called; calling earlier returns `6406`
  * `APPROVED`: Approved, wallet created. Use `companyCode` / `walletId` for subsequent business
  * `REJECTED`: Application rejected; cannot be recovered in the API scenario. Resubmit with a new `extApplicationNo`

  The `applicationStatus` semantics are consistent between query and webhook. If Party A misses the `PENDING_USER` webhook due to a network issue, the same status can be obtained by actively calling the query API.
</Note>

> Each `KeyPeopleInfo` in `profile.keyPeople.people[]` includes `peopleId` (for obtaining IDV/signing links), and two status fields `idvStatus` / `mandateStatus` (reflecting the person's real-time IDV/signing progress).

**Request Example**

```json theme={null}
{
  "applicationNo": "A202605258664"
}
```

**Response Example**

```json theme={null}
{
  "code": 1,
  "message": "Success",
  "data": {
    "applicationNo": "A202605258664",
    "extApplicationNo": "EXT20260512001",
    "applicationCreateTime": 1747094400000,
    "applicationStatus": "PENDING_USER",
    "companyCode": "",
    "walletId": 0,
    "profile": {
      "entityDetail": { "...": "..." },
      "keyPeople": {
        "people": [
          {
            "peopleId": 1000123456,
            "lastNameEn": "Lo",
            "firstNameEn": "Sang",
            "idvStatus": "UNDER_REVIEW",
            "mandateStatus": "NOT_STARTED",
            "isDirector": true,
            "isUbo": false
          }
        ]
      }
    }
  }
}
```

## 4. Get IDV Link (GetIdvLink)

**Interface Overview**

Generate a one-time H5 link for the specified `peopleId`. **A single link completes both IDV → signing**: Party B user opens the link, completes IDV first, and the page automatically proceeds to signing after IDV passes. For individuals with `isMandateUser = false`, only IDV is required; the flow ends when IDV completes.

Party A is responsible for delivering the link to Party B users (via email / SMS / QR code scan / redirect, at Party A's discretion).

<Note>
  **Precondition**: You must first receive the `ONBOARDING_APPLICATION_STATUS_NOTIFICATION` webhook with `applicationStatus = PENDING_USER` before calling this endpoint. Before this, the backend is in the company search phase; calling will return `6406` with a message to wait for the `PENDING_USER` webhook.
</Note>

**Request Parameters**

| Field Name    | Type   | M / O / CM | Description                                                                            |
| :------------ | :----- | :--------- | :------------------------------------------------------------------------------------- |
| applicationNo | string | M          | OristaPay application number                                                           |
| peopleId      | int64  | M          | Obtained from `profile.keyPeople.people[].peopleId` in the `QueryApplication` response |

**Response Parameters**

| Field Name | Type     | Description              |
| :--------- | :------- | :----------------------- |
| code       | int32    | Response code            |
| message    | string   | Response message         |
| data       | LinkData | Returned when `code = 1` |

**LinkData Field Description**

| Field Name | Type   | Description                                                              |
| :--------- | :----- | :----------------------------------------------------------------------- |
| linkUrl    | string | One-time H5 link; users complete IDV followed by signing after accessing |
| expireTime | int64  | Link expiration time, millisecond timestamp; default **24 hours**        |

**Request Example**

```json theme={null}
{
  "applicationNo": "A202605258664",
  "peopleId": 1000123456
}
```

**Response Example**

```json theme={null}
{
  "code": 1,
  "message": "Success",
  "data": {
    "linkUrl": "https://customer.oristapay.com/h5/index?code=4e0af338-9605-4353-86db-f35c4fdb7959",
    "expireTime": 1747180800000
  }
}
```

**Key Constraints**

* IDV Links are **single-use**: they expire after Party B completes the full flow (IDV + signing); unused links also expire after the timeout
* Repeated calls for the same `peopleId`: **unexpired link exists** → returns the original link; **expired** → automatically invalidates the original link and generates a new one
* Returns `6001` if the person has already completed all tasks (IDV passed, and if `isMandateUser=true`, signing also completed)

## 5. Refresh Link (RefreshIdvLink)

**Interface Overview**

When the link has expired (`expireTime < now`) or is about to expire, Party A calls this endpoint to generate a new link; the original link is immediately invalidated.

**Request Parameters**

| Field Name    | Type   | M / O / CM | Description                  |
| :------------ | :----- | :--------- | :--------------------------- |
| applicationNo | string | M          | OristaPay application number |
| peopleId      | int64  | M          | keyPeople global ID          |

**Response Parameters**

`LinkData`: Fields are the same as §4.

**Request Example**

```json theme={null}
{
  "applicationNo": "A202605258664",
  "peopleId": 1000123456
}
```

**Response Example**

Same as §4.

**Key Constraints**

* Same precondition as §4: must first receive the `PENDING_USER` webhook; otherwise returns `6406`
* The original link is immediately invalidated after this call
* Returns `6001` if the person has already completed all tasks (IDV passed and signing completed)
* Returns `6001` if the application has been in final status (`APPROVED` / `REJECTED`)

## 6. Query Company Profile (QueryCompanyProfile)

**Interface Overview**

Query the full company profile after approval by `companyCode`.

**Request Parameters**

| Field Name  | Type   | M / O / CM | Description                                                               |
| :---------- | :----- | :--------- | :------------------------------------------------------------------------ |
| companyCode | string | M          | Company code (obtained from `QueryApplication` or Webhook after approval) |

**Response Parameters**

| Field Name | Type           | Description                                                                           |
| :--------- | :------------- | :------------------------------------------------------------------------------------ |
| code       | int32          | Response code                                                                         |
| message    | string         | Response message                                                                      |
| data       | ProfileFormDto | Full company profile. See [Company Profile Form](#company-profile-form) for structure |

**Request Example**

```json theme={null}
{
  "companyCode": "HKM12389h"
}
```

**Response Example**

```json theme={null}
{
  "code": 1,
  "message": "Success",
  "data": {
    "entityDetail": { "...": "..." },
    "businessDetail": { "...": "..." },
    "shareholder": { "...": "..." },
    "keyPeople": { "...": "..." }
  }
}
```

**Key Constraints**

* Only companies with approved status (`applicationStatus = APPROVED`) can be queried; unapproved companies return `6001` + message "Profile is not approved yet."
* `companyCode` must belong to the current Party A; otherwise returns `6005`
* The data reflects the latest OristaPay review result, which may differ from the submitted `profile` (reviewers may supplement or correct certain fields)

# Callback

The callback request body, signature verification, and response requirements are fully consistent with the [ISV Transaction API · Callback](./transaction-api#callback). This document only defines the new `OpenBizType` values and payload fields.

## 1. New OpenBizType Enums

| Enum Value                                    | Trigger                                                           |
| :-------------------------------------------- | :---------------------------------------------------------------- |
| ONBOARDING\_APPLICATION\_STATUS\_NOTIFICATION | Application status change (PENDING\_USER / APPROVED / REJECTED)   |
| ONBOARDING\_KEY\_PEOPLE\_IDV\_FAILED          | A key person's IDV failed (vendor failure or manual case failure) |

## 2. Callback Data Types

### 2.1 Application Status Change (OnboardingApplicationStatusNotification)

**Trigger Timing (only key review nodes; individual node changes are not pushed)**

| applicationStatus | Trigger Timing                                                                                  | Party A Action                                                                                                                                                                                               |
| :---------------- | :---------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PENDING_USER`    | Company search passed → keyPeople IDV/signing can begin                                         | Only now (and only now) can `GetIdvLink` be called (calling earlier returns `6406`)                                                                                                                          |
| `APPROVED`        | Application fully approved, wallet created                                                      | Retrieve `companyCode` / `walletId` for subsequent business                                                                                                                                                  |
| `REJECTED`        | Application rejected in final state (includes company search failure, compliance failure, etc.) | Rejected applications **cannot be recovered** in the API scenario. Party A must submit a new application (do not reuse the original `extApplicationNo`, or the idempotency check will return the old record) |

<Note>
  * Newly submitted applications (first entering `UNDER_REVIEW` status) do not trigger a separate webhook. Party A knows the application is accepted through the synchronous `SubmitApplication` response
  * Node-level changes (`NameScreening → RiskScreening`, etc.) are **no longer pushed**; only available through the `QueryApplication` API
  * Duplicate pushes of the same `applicationStatus` should be treated idempotently (deduplicated by `X-Nonce`)
</Note>

**Field Description**

| Field Name        | Type   | Description                                                             |
| :---------------- | :----- | :---------------------------------------------------------------------- |
| applicationNo     | string | OristaPay application number                                            |
| extApplicationNo  | string | Party A application number                                              |
| applicationStatus | string | `PENDING_USER` / `APPROVED` / `REJECTED`                                |
| companyCode       | string | Company code (populated on `APPROVED`; empty string for other statuses) |
| walletId          | int64  | Wallet ID (populated after wallet creation; `0` for other statuses)     |
| eventTime         | int64  | Event trigger time, millisecond timestamp                               |

**Example (Company Search Passed, IDV Can Begin)**

```json theme={null}
{
  "applicationNo": "A202605258664",
  "extApplicationNo": "EXT20260512001",
  "applicationStatus": "PENDING_USER",
  "companyCode": "",
  "walletId": 0,
  "eventTime": 1747094400000
}
```

**Example (Approved)**

```json theme={null}
{
  "applicationNo": "A202605258664",
  "extApplicationNo": "EXT20260512001",
  "applicationStatus": "APPROVED",
  "companyCode": "HKM12389h",
  "walletId": 12345678901234,
  "eventTime": 1747104400000
}
```

**Example (Rejected / Company Search Failed)**

```json theme={null}
{
  "applicationNo": "A202605258664",
  "extApplicationNo": "EXT20260512001",
  "applicationStatus": "REJECTED",
  "companyCode": "",
  "walletId": 0,
  "eventTime": 1747094500000
}
```

### 2.2 keyPeople IDV Failure (OnboardingKeyPeopleIdvFailedNotification)

Pushed when a key person's IDV fails. Upon receiving this, Party A can have Party B retry IDV: call `RefreshIdvLink` to get a new link for Party B.

**Trigger**

* IDV failure

**Field Description**

| Field Name       | Type   | Description                                                 |
| :--------------- | :----- | :---------------------------------------------------------- |
| applicationNo    | string | OristaPay application number                                |
| extApplicationNo | string | Party A application number                                  |
| peopleId         | string | keyPeople global ID (as string, preserving int64 precision) |
| eventTime        | int64  | Event trigger time, millisecond timestamp                   |

**Example**

```json theme={null}
{
  "applicationNo": "A202605258664",
  "extApplicationNo": "EXT20260512001",
  "peopleId": "1000123456",
  "eventTime": 1747100100000
}
```

# Company Profile Form

`profile` is the core payload for submitting an onboarding application (`SubmitApplication`), and also the core data returned by query APIs. The structure varies by **business type** (partnership / limited company / sole proprietorship) and **place of incorporation**.

## 1. Top-Level Structure

```text theme={null}
profile = {
  entityDetail,                  // Basic entity info (required)
  businessDetail,                // Business info (required)
  shareholder,                   // Shareholder info (required for limited companies only)
  keyPeople,                     // Key people (required, includes IDV/signing subjects)
  accountOpeningQuestionnaire    // Account opening questionnaire (required)
}
```

## 2. Support Matrix

| Incorporation \ Business Type    | Limited Company | Partnership | Sole Proprietorship |
| :------------------------------- | :-------------- | :---------- | :------------------ |
| Hong Kong (`HKG`)                | ✓               | ✓           | ✓                   |
| Other (non-HKG, and **non-CHN**) | ✓               | ✗           | ✗                   |

## 3. Common Sub-structures

### 3.1 businessDetail.list\[] Element

| Field             | Type              | M / O / CM | Description                                                                                                                                  |
| :---------------- | :---------------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
| subIndustryCode   | string(10)        | M          | Sub-industry itemId, **must exist in the OristaPay foundation industry dictionary**. See [Industry Code](/cn/others-resources/industry-code) |
| yearsInBusiness   | string            | M          | String form of the code. See [Years in Business](#) appendix                                                                                 |
| businessLocations | Array\<string>(3) | M          | Business location list, max 3. See [Country/Region Code](/cn/others-resources/country-code)                                                  |
| lastYearSales     | string            | M          | String form of the code. See [Last Year Sales](#) appendix                                                                                   |
| industryDetails   | string(256)       | M          | Industry supplementary description                                                                                                           |

> Party A only needs to pass `subIndustryCode`. The primary `industryCode` is auto-populated by the server via dictionary lookup; any value passed by Party A will be overwritten.

### 3.2 KeyPeopleInfo Common Fields

All business types use `KeyPeopleInfo` for `keyPeople.people[]`. Common submission fields:

| Field                         | Type           | M / O / CM | Description                                                                                                                                                                   |
| :---------------------------- | :------------- | :--------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| lastNameEn                    | string(128)    | CM         | Either Chinese or English name required                                                                                                                                       |
| firstNameEn                   | string(128)    | CM         | Either Chinese or English name required                                                                                                                                       |
| nameZh                        | string(128)    | CM         | Either Chinese or English name required                                                                                                                                       |
| areaCode                      | string(8)      | O          | Phone area code                                                                                                                                                               |
| mobileNumber                  | string(32)     | O          | Mobile number                                                                                                                                                                 |
| email                         | string(128)    | O          | Email address                                                                                                                                                                 |
| gender                        | int32          | M          | `1` Male / `2` Female                                                                                                                                                         |
| birthdate                     | string         | M          | Date of birth, `YYYY-MM-DD`                                                                                                                                                   |
| region                        | string         | M          | Country/region of ID document. See [Country/Region Code](/cn/others-resources/country-code)                                                                                   |
| idType                        | int32          | M          | `1` Mainland China ID / `2` Hong Kong ID / `3` Passport                                                                                                                       |
| idNumber                      | string         | M          | ID document number                                                                                                                                                            |
| isMandateUser                 | boolean        | M          | Whether this person is a signatory. **The count of `isMandateUser=true` in keyPeople must be ≥ `keyPeople.quorum`** (minimum quorum). Typically some or all directors         |
| ownedSharesPercent            | double(6,3)    | CM         | Shareholding percentage (0\~100, 3 decimal places). **Required only when `isUbo=true`**                                                                                       |
| isWalletAdmin                 | boolean        | M          | Whether this person is a wallet admin. **Exactly one person in keyPeople must have `isWalletAdmin=true`**; this person is automatically set as the company wallet super admin |
| overseaWorkCertificateFileKey | Array\<string> | CM         | Overseas work certificate fileKey list. **Required only when `isWalletAdmin=true` and `region=CHN`** (Mainland China resident); max 5                                         |

<Note>
  **Two typical wallet admin configurations**:

  1. **Combined role**: A director / UBO also checks `isWalletAdmin=true` (one person, two roles)
  2. **Separate role**: Add a **wallet-admin-only** person to the `keyPeople` list (set `isDirector` / `isUbo` / `isPartner` / `isOwner` all to `false`, only `isWalletAdmin=true`). This person must go through IDV
</Note>

> The "Mainland China ID Card" in `idType` is a **personal document** enum, distinct from "place of incorporation". When the incorporation place is HKG but a director/shareholder is a Mainland China resident, this `idType` is used normally.

### 3.3 Runtime Fields Written Back by OristaPay

Party A must **NOT populate** these fields on submission; they carry write-back results from OristaPay when read via `QueryApplication`.

| Field         | Type   | Description                                                                                                              |
| :------------ | :----- | :----------------------------------------------------------------------------------------------------------------------- |
| peopleId      | int64  | OristaPay-generated global person ID; must be passed when obtaining IDV/signing links                                    |
| idvStatus     | string | IDV progress: `NOT_STARTED` / `UNDER_REVIEW` vendor passed + manual review / `COMPLETED` manual passed / `FAILED`        |
| mandateStatus | string | Signing status (meaningful only when `isMandateUser = true`): `NOT_STARTED` / `SIGNED`; empty string for non-signatories |

### 3.4 Account Opening Questionnaire

All business types must include this module in `profile`.

\| Field | Type | M / O / CM | Description |                                 | | :-- |:--------------| :-- |:---------------------------------------------------------------------------------------------------------------------------| | accountOpeningPurposes | Array\<enum> | M | Account opening purposes, select 1-2: `PAYMENT_FOR_HK_BUSINESS` / `STORE_DIGITAL_ASSETS_FOR_HK_BUSINESS` | | expectedCounterparties | string(256)   | O | Expected business counterparties (free text) | | expectedMonthlyVolume | enum          | M | Expected monthly volume (HKD), single choice: `BELOW_1M_HKD` / `FROM_1M_TO_2_5M_HKD` / `FROM_2_5M_TO_5M_HKD` / `FROM_5M_TO_10M_HKD` / `ABOVE_10M_HKD` | | handleClientMoney | string        | M | Whether client money is handled (YES/NO) |

**Example**

```json theme={null}
"accountOpeningQuestionnaire": {
  "accountOpeningPurposes": ["PAYMENT_FOR_HK_BUSINESS", "STORE_DIGITAL_ASSETS_FOR_HK_BUSINESS"],
  "expectedCounterparties": "Major HK retail merchants",
  "expectedMonthlyVolume": "FROM_2_5M_TO_5M_HKD",
  "handleClientMoney": "NO"
}
```

## 4. Partnership Company (Hong Kong only)

**entityDetail**

| Field            | Type           | M / O / CM | Description                                                                    |
| :--------------- | :------------- | :--------- | :----------------------------------------------------------------------------- |
| businessType     | int32          | M          | `2` Partnership                                                                |
| incorpPlace      | string         | M          | Place of incorporation, only `HKG` supported                                   |
| brNumber         | string(32)     | M          | Business Registration certificate number                                       |
| brFileKey        | string(128)    | M          | Business Registration file fileKey                                             |
| incorpDate       | string         | M          | Date of incorporation, `YYYY-MM-DD`                                            |
| website          | string(512)    | O          | Website URL                                                                    |
| regPlace         | string         | M          | Registered place. See [Country/Region Code](/cn/others-resources/country-code) |
| regAddress       | string(256)    | M          | Registered address, English / numbers / spaces only                            |
| operatingPlace   | string         | M          | Operating place                                                                |
| operatingAddress | string(256)    | M          | Operating address                                                              |
| nameEn           | string(128)    | M          | Company name in English                                                        |
| nameZh           | string(128)    | O          | Company name in Chinese                                                        |
| paFileKey        | Array\<string> | M          | Partnership Agreement files, max 5                                             |

**keyPeople**: 2-6 partners. `KeyPeopleInfo` adds the following on top of common fields:

| Field     | Type | M / O / CM | Description                                     |
| :-------- | :--- | :--------- | :---------------------------------------------- |
| isPartner | bool | M          | Whether this person is a partner, always `true` |

**Submission Example**

```json theme={null}
{
  "entityDetail": {
    "businessType": 2,
    "incorpPlace": "HKG",
    "brNumber": "br-number-partner",
    "incorpDate": "2025-05-01",
    "website": "www.example.com",
    "regPlace": "HKG",
    "regAddress": "Example partner register address",
    "operatingPlace": "HKG",
    "operatingAddress": "Example partner operating address",
    "nameEn": "Example Partnership",
    "nameZh": "示例合伙公司",
    "brFileKey": "4e0af338-9605-4353-86db-f35c4fdb7959.jpg",
    "paFileKey": ["06993a52-3069-41c9-a048-1601a3dfade4.jpg"]
  },
  "businessDetail": {
    "list": [
      {
        "subIndustryCode": "I300734",
        "yearsInBusiness": "1",
        "businessLocations": ["HKG"],
        "lastYearSales": "1",
        "industryDetails": "Digital asset platform"
      }
    ]
  },
  "keyPeople": {
    "quorum": 2,
    "directorNum": 0,
    "people": [
      {
        "lastNameEn": "Lo",
        "firstNameEn": "Sang",
        "nameZh": "罗生",
        "areaCode": "852",
        "mobileNumber": "61234567",
        "email": "lo@example.com",
        "isPartner": true,
        "isMandateUser": true,
        "isWalletAdmin": true
      },
      {
        "lastNameEn": "Yeung",
        "firstNameEn": "Sang",
        "nameZh": "杨生",
        "areaCode": "852",
        "mobileNumber": "61234568",
        "email": "yeung@example.com",
        "isPartner": true,
        "isMandateUser": true,
        "isWalletAdmin": false
      }
    ]
  },
  "accountOpeningQuestionnaire": {
    "accountOpeningPurposes": ["PAYMENT_FOR_HK_BUSINESS"],
    "expectedMonthlyVolume": "FROM_2_5M_TO_5M_HKD",
    "handleClientMoney": false
  }
}
```

## 5. Limited Company (Hong Kong)

**entityDetail**

| Field                   | Type           | M / O / CM | Description                                         |
| :---------------------- | :------------- | :--------- | :-------------------------------------------------- |
| businessType            | int32          | M          | `1` Limited Company                                 |
| incorpPlace             | string         | M          | Place of incorporation, `HKG`                       |
| ciNumber                | string(32)     | M          | Certificate of Incorporation number                 |
| ciFileKey               | string(128)    | M          | Certificate of Incorporation file                   |
| brNumber                | string(32)     | M          | Business Registration certificate number            |
| brFileKey               | string(128)    | M          | Business Registration file                          |
| incorpDate              | string         | M          | Date of incorporation, `YYYY-MM-DD`                 |
| website                 | string(512)    | O          | Website URL                                         |
| regPlace                | string         | M          | Registered place of company                         |
| regAddress              | string(256)    | M          | Registered address of company                       |
| operatingPlace          | string         | M          | Operating place                                     |
| operatingAddress        | string(256)    | M          | Operating address                                   |
| nameEn                  | string(128)    | M          | Company name in English                             |
| nameZh                  | string(128)    | O          | Company name in Chinese                             |
| isFinancialInstitute    | int32          | M          | `1` Regulated / `2` Non-regulated                   |
| financialRegulatorPlace | string         | CM         | Required if regulated                               |
| financialRegulator      | string(256)    | CM         | Required if regulated                               |
| financialLicenseType    | string(128)    | CM         | Required if regulated                               |
| regulatedProofKey       | Array\<string> | CM         | Regulatory proof documents, max 5                   |
| isListed                | int32          | M          | `1` Listed / `2` Non-listed                         |
| listingPlace            | string(3)      | CM         | Required if listed                                  |
| listingExchange         | string(256)    | CM         | Required if listed                                  |
| stockCode               | string(128)    | CM         | Required if listed                                  |
| isGovOwned              | int32          | M          | `1` Government-owned / `2` Non-government-owned     |
| ownedGovPlace           | string(3)      | CM         | Required if government-owned                        |
| maFileKey               | Array\<string> | M          | Memorandum and Articles of Association files, max 5 |

**shareholder** (choose either `list` or `shareholderFileKey`)

| Field              | Type                   | M / O / CM | Description                                                                       |
| :----------------- | :--------------------- | :--------- | :-------------------------------------------------------------------------------- |
| list               | Array\<ShareholderDto> | Either     | Structured shareholder list                                                       |
| shareholderFileKey | Array\<string>         | Either     | Shareholding structure diagram fileKey list; passing this does not require `list` |

> Shareholder structure is required only for "Limited Company" when `isFinancialInstitute=2` / `isListed=2` / `isGovOwned=2` (i.e., none are exempted).

**ShareholderDto**

| Field              | Type        | M / O / CM | Description                                                                                                                                  |
| :----------------- | :---------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
| shareholderId      | int64       | M          | Unique shareholder identifier, generated by the requester                                                                                    |
| sameId             | int64       | O          | Marks "same shareholder appearing multiple times in structure"; default `0`                                                                  |
| parentId           | int64       | O          | Parent `shareholderId`; use `0` for the first layer                                                                                          |
| shareholderType    | int32       | M          | `1` Individual shareholder / `2` Corporate shareholder                                                                                       |
| businessType       | int32       | CM         | Corporate shareholder business type; required when `shareholderType=2`                                                                       |
| incorpPlace        | string      | CM         | Corporate shareholder place of incorporation. For partnership/sole proprietorship shareholders, only `HKG` supported; **`CHN` not accepted** |
| ownedSharesPercent | double(6,3) | M          | Shareholding percentage of parent                                                                                                            |
| companyNameEn      | string(128) | CM         | Corporate shareholder name in English (either Chinese or English name required)                                                              |
| companyNameZh      | string(128) | CM         | Corporate shareholder name in Chinese (either Chinese or English name required)                                                              |
| firstNameEn        | string(128) | CM         | Individual shareholder first name in English (either Chinese or English name required)                                                       |
| lastNameEn         | string(128) | CM         | Individual shareholder last name in English (either Chinese or English name required)                                                        |
| nameZh             | string(128) | CM         | Individual shareholder name in Chinese (either Chinese or English name required)                                                             |

**keyPeople**

| Field       | Type                  | M / O / CM | Description                                                           |
| :---------- | :-------------------- | :--------- | :-------------------------------------------------------------------- |
| quorum      | int32                 | M          | Minimum quorum, 1-99                                                  |
| directorNum | int32                 | M          | Number of directors (must equal the count of `isDirector=true`), 1-99 |
| people      | Array\<KeyPeopleInfo> | M          | List all UBOs and directors of the company                            |

`KeyPeopleInfo` adds the following on top of common fields:

| Field      | Type    | M / O / CM | Description                                               |
| :--------- | :------ | :--------- | :-------------------------------------------------------- |
| isDirector | boolean | M          | Whether this person is a director                         |
| isUbo      | boolean | M          | Whether this person is an Ultimate Beneficial Owner (UBO) |

## 6. Limited Company (Non-Hong Kong)

Most fields are identical to §5. Differences are listed below (unlisted fields match §5):

| Field       | Type           | M / O / CM | Description                                                                                            |
| :---------- | :------------- | :--------- | :----------------------------------------------------------------------------------------------------- |
| incorpPlace | string         | M          | Non-`HKG` country/region code; **`CHN` not accepted**                                                  |
| brNumber    | string(32)     | O          | Business Registration certificate number (optional for non-HK regions)                                 |
| brFileKey   | string(128)    | O          | Business Registration file (optional for non-HK regions)                                               |
| coiFileKey  | Array\<string> | CM         | Certificate of Incumbency. Required when `incorpPlace` is `BMU` / `WSM` / `SYC` / `CYM` / `VGB`, max 5 |

## 7. Sole Proprietorship Company (Hong Kong only)

**entityDetail**

| Field                | Type        | M / O / CM | Description                              |
| :------------------- | :---------- | :--------- | :--------------------------------------- |
| businessType         | int32       | M          | `3` Sole Proprietorship                  |
| incorpPlace          | string      | M          | Only `HKG` supported                     |
| brNumber             | string(32)  | M          | Business Registration certificate number |
| brFileKey            | string(128) | M          | Business Registration file               |
| incorpDate           | string      | M          | Date of incorporation, `YYYY-MM-DD`      |
| website              | string(512) | O          | Website URL                              |
| regPlace             | string      | M          | Registered place                         |
| regAddress           | string(256) | M          | Registered address                       |
| operatingPlace       | string      | M          | Operating place                          |
| operatingAddress     | string(256) | M          | Operating address                        |
| nameEn               | string(128) | M          | Company name in English                  |
| nameZh               | string(128) | O          | Company name in Chinese                  |
| isFinancialInstitute | int32       | O          | `1` Regulated / `2` Non-regulated        |

**keyPeople**: Exactly one owner. `KeyPeopleInfo` adds the following on top of common fields:

| Field   | Type    | M / O / CM | Description                                                        |
| :------ | :------ | :--------- | :----------------------------------------------------------------- |
| isOwner | boolean | M          | Whether this person is the sole proprietorship owner (exactly one) |

## 8. File Key Field Summary

Upload files via the file upload endpoint to obtain `fileKey`, then fill them into `profile`.

| Profile Field                                        | File Description                                          | Type               |
| :--------------------------------------------------- | :-------------------------------------------------------- | :----------------- |
| entityDetail.brFileKey                               | Business Registration                                     | string             |
| entityDetail.ciFileKey                               | Certificate of Incorporation                              | string             |
| entityDetail.maFileKey\[]                            | Memorandum & Articles of Association (M\&A)               | Array\<string> ≤ 5 |
| entityDetail.paFileKey\[]                            | Partnership Agreement                                     | Array\<string> ≤ 5 |
| entityDetail.kycFileKey\[]                           | KYC Proof                                                 | Array\<string> ≤ 5 |
| entityDetail.regulatedProofKey\[]                    | Regulated Financial Institution Proof                     | Array\<string> ≤ 5 |
| entityDetail.coiFileKey\[]                           | Certificate of Incumbency                                 | Array\<string> ≤ 5 |
| keyPeople.people\[].idFileKey\[]                     | Personal ID photo                                         | Array\<string> ≤ 5 |
| keyPeople.people\[].overseaWorkCertificateFileKey\[] | Overseas work certificate (required for CHN wallet admin) | Array\<string> ≤ 5 |

File types and limits: `jpeg / jpg / png / pdf / gif`, max 20 MB per file.

# Error Codes

## 1. Business Error Codes

| code   | Meaning                   | Typical ISV Onboarding Trigger                                                                                                 |
| :----- | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------- |
| `1`    | Success                   | —                                                                                                                              |
| `6001` | General business failure  | Unclassified business error                                                                                                    |
| `6002` | Parameter error           | Missing required fields or type mismatch                                                                                       |
| `6003` | Order not found           | `applicationNo` / `extApplicationNo` not found; `peopleId` not found                                                           |
| `6005` | No permission             | Party A has not enabled ISV onboarding; or Party A queries another party's `applicationNo`                                     |
| `6406` | Request being processed   | Company search not yet passed; wait for `PENDING_USER` webhook before calling `GetIdvLink` / `RefreshIdvLink`                  |
| `6801` | Duplicate application     | Same `extApplicationNo` with different body; or same Party B (CI/BR number) already has an active application                  |
| `6802` | Profile error             | `profile` validation failed, with `errors[]` list (see [Profile Error Descriptions](#3-profile-error-descriptions-code--6802)) |
| `6803` | Application limit reached | Party A has exceeded the account opening quota for the current period (if applicable)                                          |

## 2. Per-Endpoint Error Code Reference

### 2.1 SubmitApplication

| Scenario                                                   | Response                                                   |
| :--------------------------------------------------------- | :--------------------------------------------------------- |
| Submission successful                                      | `code = 1` + `data.applicationNo`                          |
| Missing required fields / type error                       | `code = 6002`                                              |
| Party A has not enabled ISV onboarding                     | `code = 6005`                                              |
| Same `extApplicationNo`, same body                         | `code = 1` + returns original `applicationNo` (idempotent) |
| Same `extApplicationNo`, different body                    | `code = 6801`                                              |
| Same Party B (CI/BR number) already has active application | `code = 6801`                                              |
| `profile` validation failed                                | `code = 6802` + `errors[]`                                 |

### 2.2 QueryApplication

| Scenario                                                | Response            |
| :------------------------------------------------------ | :------------------ |
| Query successful                                        | `code = 1` + `data` |
| Neither `applicationNo` nor `extApplicationNo` provided | `code = 6002`       |
| Application not found                                   | `code = 6003`       |
| Application does not belong to current Party A          | `code = 6005`       |

### 2.3 GetIdvLink

| Scenario                                                                  | Response                                                                                                               |
| :------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------- |
| Successful                                                                | `code = 1` + `data.linkUrl` + `expireTime`                                                                             |
| `applicationNo` not found / not owned by current Party A                  | `code = 6003` / `6005`                                                                                                 |
| `peopleId` not found / not belonging to this application                  | `code = 6003`                                                                                                          |
| All tasks for this person already completed                               | `code = 6001`                                                                                                          |
| Application already in final state (`APPROVED` / `REJECTED`)              | `code = 6001`                                                                                                          |
| **Company search not yet passed** (calling before `PENDING_USER` webhook) | `code = 6406` + `message`: "Company search not passed yet. Wait for PENDING\_USER webhook before requesting IDV link." |

### 2.4 RefreshIdvLink

| Scenario                                                              | Response               |
| :-------------------------------------------------------------------- | :--------------------- |
| Successful                                                            | `code = 1`             |
| `applicationNo` / `peopleId` not found / not owned by current Party A | `code = 6003` / `6005` |
| All tasks for this person already completed                           | `code = 6001`          |
| Application already in final state                                    | `code = 6001`          |
| Company search not yet passed (calling before `PENDING_USER` webhook) | `code = 6406`          |

### 2.5 QueryCompanyProfile

| Scenario                                                            | Response                                               |
| :------------------------------------------------------------------ | :----------------------------------------------------- |
| Successful                                                          | `code = 1` + `data`                                    |
| `companyCode` not found                                             | `code = 6003`                                          |
| Company corresponding to `companyCode` not owned by current Party A | `code = 6005`                                          |
| Company not yet approved                                            | `code = 6001` + message "Profile is not approved yet." |

## 3. Profile Error Descriptions (`code = 6802`)

The complete list is identical to [ISV Transaction API · Profile Error Descriptions](./transaction-api#profile-error-description), grouped by module:

* `[Business details]`
* `[Entity details]`
* `[Key people]`
* `[Shareholder]`
* `[Others]`

Additional rejection scenarios specific to this API set:

* `[Entity details]Place of incorporation not supported: CHN` — `incorpPlace` / `regPlace` / `operatingPlace` at entity level must not be CHN
* `[Entity details]Place of incorporation not supported: <sanctioned country code>` — Sanctioned country
* `[Entity details]Partnership / Sole proprietorship only support incorpPlace=HKG` — Partnership / sole proprietorship is supported in Hong Kong only
* `[Key people]Nationality not supported: <country code>` — keyPeople nationality is a sanctioned country (CHN is exempted)
* `[Wallet Admin]Work certification is necessary` — Wallet admin with region=CHN did not provide `overseaWorkCertificateFileKey`
