> For the complete documentation index, see [llms.txt](https://www.pionex.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.pionex.com/docs/api-docs/institution-api/onboarding.md).

# Start Here: Onboarding & Signing Guide

> This document is the **English** onboarding-flow and encryption/signing-algorithm guide for the **Pionex Institution Open API (v2)**. It helps institutional integrators understand the end-to-end main line — from registering a public key, creating sub-accounts, and completing KYB, through initiating deposits/payouts/conversions — and implement request signing accordingly. **This is not the complete API endpoint reference** — for the full set of endpoints, fields, and request/response structures, see `openapi_institution_v2.yaml` in the same directory.
>
> For the Chinese version, see `openapi_institution_v2_onboarding.md`.

An institution uses **its own API Key (a pair of RSA or Ed25519 keys)** to onboard and operate the sub-accounts (`userId`) held under its name: fiat deposits/payouts, stablecoin conversion, on-chain assets, and trading-account balances. v2 uses **public-key signature authentication** (RSA or Ed25519) and identifies the target sub-account by its **`userId` (UUID)**.

| General Information | Value                                                                                                                                                  |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Base URL            | `https://api.pionex.com`                                                                                                                               |
| Protocol            | HTTPS                                                                                                                                                  |
| Data format         | JSON                                                                                                                                                   |
| Field naming        | camelCase (e.g. `userId`, `clientOrderId`)                                                                                                             |
| Amounts             | decimal strings (e.g. `"100.50"`) — never floats, never smallest-unit integers                                                                         |
| Timestamps          | In response bodies these are **millisecond** Unix timestamps (`int64`); the `timestamp` query parameter used for signing is in **seconds** (see below) |
| Currency codes      | ISO 4217 uppercase (`"USD"`); country codes ISO 3166-1 alpha-2 uppercase (`"US"`)                                                                      |

***

## Part 1: Onboarding Flow

### 1. End-to-End Main Line

Onboarding has two stages:

* **Phases 1–4 are the onboarding main line (executed in order, none can be skipped):** authentication → create sub-account → platform KYB → channel onboarding.
* Once a sub-account is usable, **deposit / fiat payout / on-chain withdrawal / conversion / balance query are independent capabilities that can be called in any order.**

![Pionex Institution Open API v2 — onboarding flow](https://4194290415-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFR2ceef8Tg60c2lMiqG0%2Fuploads%2Fgit-blob-19d1e2a802187f802357a716f9e2d82b58a2cc91%2Fopenapi_institution_v2_onboarding.svg?alt=media)

> **Key gate:** All `wire/*` endpoints (channel deposit onboarding, deposit accounts/orders, payout accounts, payouts) require the target `userId`'s **platform KYB to first reach `APPROVED`**; otherwise they return `P_PAY_OPEN_API_INTERNAL_KYB_NOT_APPROVED`.

### 2. Step-by-Step Flow

#### Phase 1 — Authentication

Register your **public key** with Webot, obtain an API Key (of the form `webot_xxxxxxxx`), and implement request signing. You keep the private key yourself. See [Part 2](#part-2-encryption--signing-algorithm-authentication--signing) for details.

#### Phase 2 — Create Sub-Account

```
POST /api/v2/institution/user/create
```

Create a sub-account held under your institution's name, idempotent by `clientId`. Request body: `clientId` (required, idempotency key, ≤64), `email` (required, ≤64), `entityType` (required, `CORPORATE`/`INDIVIDUAL`), `remark` (optional). The response returns `data.userId`. All subsequent operations targeting this sub-account use this `userId`.

> If `P_PAY_OPEN_API_SUB_USER_ACCOUNT_CREATE_FAILED` is returned, the sub-account has been created but not fully initialized; `data.userId` returns the already-created id — resend an **exactly identical** request to complete initialization.

Use `GET /api/v2/institution/users` to list all sub-accounts and obtain their `userId`.

#### Phase 3 — Platform KYB

```
POST /api/v2/institution/kyb/create      # submit
GET  /api/v2/institution/kyb             # query status
```

Submit the company/representative information once. It **must reach `APPROVED`** before any channel deposit onboarding can proceed. A `userId` has only one platform KYB application; resubmitting while under review updates it, and once it is `APPROVED` it can no longer be submitted (returns `P_PAY_OPEN_API_KYB_ALREADY_APPROVED`).

Submission body structure:

```json
{
  "userId": "88001234-....",
  "subject": { "fields": [ { "key": "subject.legalNameEn", "value": "Acme Ltd." } ] },
  "representatives": [ { "representativeRef": "PERSON-01", "fields": [ { "key": "representative.firstName", "value": "..." } ] } ],
  "documents": [ { "purpose": "subject.sourceOfFundsProof", "fileId": "...", "scope": "SUBJECT" },
                 { "purpose": "representative.passport", "fileId": "...", "scope": "REPRESENTATIVE", "representativeRef": "PERSON-01" } ]
}
```

On submission, passing validation (key validity + unconditional required fields + enums) returns `SUBMITTED`; otherwise it returns `P_PAY_OPEN_API_INVALID_ARGUMENT` and nothing is persisted.

#### Phase 4 — Channel Onboarding (Channel KYB)

```
GET  /api/v2/institution/wire/deposit/account/requirements   # query requirements first
POST /api/v2/institution/wire/deposit/account/create         # then submit per requirements
GET  /api/v2/institution/wire/deposit/account                # query onboarding status
```

After platform KYB is approved, onboard a deposit account for a given channel. **`channel` currently supports `fvbank` and `straitsx`.** Channel fields are **dynamic** — **call `requirements` first**, then fill in the returned `Requirement` items; do not hardcode a field list. After submission, only the onboarding `status` is returned, not an itemized list of what is missing — call `requirements` again to learn what is still missing. **Documents must be submitted in full every time.**

#### Independent Capabilities After the Sub-Account Is Usable

| Capability          | Key endpoints                                                | Notes                                                                                                 |
| ------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| Deposit             | `GET .../wire/deposit/accounts`, `.../orders`, `.../order`   | Query deposit accounts (to obtain the remittance `instructions[]`) and deposit orders                 |
| Fiat Payout         | `.../wire/payout/account/*` → `.../wire/payout/order/create` | Create the payee account first, **wait until its status reaches `AVAILABLE`**, then submit the payout |
| On-chain Withdrawal | `POST .../asset/withdraw`, `POST .../addressBook`            | **Whitelist the address / add it to the address book first**, then withdraw                           |
| Convert             | `POST .../convert/order/create`                              | Stablecoin / currency conversion                                                                      |
| Balances            | `GET .../account/balances`                                   | Trading-account balances                                                                              |

### 3. Two-Step KYB: Field-Source Differences Between Platform KYB and Channel KYB

Onboarding is essentially **two-step KYB**, and the two draw their fields from different sources — this is the most commonly confused point:

| Dimension                               | Platform KYB (`POST /kyb/create`)                                                                                                                                       | Channel KYB (`wire/deposit/account/*`)                                                                                                                        |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Field source                            | **Fixed, documented field list** — fill in per this document's [Platform KYB Field Reference](#appendix-platform-kyb-field-reference), not obtained through an endpoint | **Dynamic** — call `GET .../wire/deposit/account/requirements` first, then fill in the returned `Requirement` items                                           |
| Precondition                            | None (it is step 3 of the main line)                                                                                                                                    | Platform KYB must be `APPROVED` first                                                                                                                         |
| `channel`                               | Not involved                                                                                                                                                            | Currently `fvbank` / `straitsx`                                                                                                                               |
| How to decide whether to submit an item | By the field's M/C/O marker (Mandatory/Conditional/Optional)                                                                                                            | By `Requirement.mode`: `REQUIRED`/`OPTIONAL`/`CONDITIONAL` (when `CONDITIONAL` is uncertain, submitting is recommended; the channel makes the final decision) |
| country / businessType                  | Provided explicitly in `subject.*`                                                                                                                                      | **Do not send** `country`/`businessType`; they inherit the platform KYB's `subject.country`/`subject.businessType`                                            |
| Documents                               | `documents[]`; upload first to obtain a `fileId`, then reference it                                                                                                     | Likewise upload first to obtain a `fileId`; items with `kind=DOCUMENT` are referenced via `fileId`, and **documents must be submitted in full every time**    |

Channel `Requirement` structure: `{ key, kind (FIELD|DOCUMENT), mode (REQUIRED|OPTIONAL|CONDITIONAL), label, regex, example, enumValues[] }`. `regex`/`example`/`enumValues` are used only for client-side validation and hints. `requirements` returns only the items the channel is **still missing** (already-provided items are omitted); representatives fill in their own `fields`/`documents`, grouped by `representativeRef`.

### 4. State-Machine Quick Reference

Understanding each state helps you judge how the flow is progressing:

| State machine                                      | Values                                                                                                                        | Notes                                                   |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| **Platform KYB status**                            | `SUBMITTED` / `PENDING` / `SUPPLEMENT_REQUIRED` / `APPROVED` / `REJECTED`                                                     | Only `APPROVED` unlocks `wire/*`                        |
| **Channel onboarding status**                      | `NOT_CREATED` / `SUBMITTED` / `IN_REVIEW` / `ACTION_REQUIRED` / `APPROVED` / `REJECTED`                                       | `NOT_CREATED` means not yet onboarded (not an error)    |
| **Deposit account status** (DepositAccount.status) | `PENDING` (not yet remittable) / `ACTIVE` (remittable) / `UNAVAILABLE` / `CLOSED`                                             |                                                         |
| **Deposit order status** (DepositOrderStatus)      | `PENDING` / `CREDITED` (credited but not settled) / `COMPLETED` (terminal) / `FAILED` (terminal) / `CANCELED` (terminal)      |                                                         |
| **Payout account status** (PayoutAccount.status)   | `IN_REVIEW` / `AVAILABLE` / `NEEDS_UPDATE` / `UNAVAILABLE` / `DELETED`                                                        | Must reach `AVAILABLE` before a payout can be initiated |
| **Payout order status** (PayoutOrderStatus)        | `PENDING` / `IN_REVIEW` / `PROCESSING` / `COMPLETED` (terminal) / `FAILED` (terminal) / `RETURNED` / `REFUNDING` / `REFUNDED` |                                                         |

> Business failures are returned as HTTP `200` + `result: false`. Only the authentication layer is an exception: `401` (authentication failure), `400` (request body unreadable). Terminal orders in a non-success state carry a `reason` object `{ code, message, retryable }`.

***

## Part 2: Encryption / Signing Algorithm (Authentication & Signing)

Except for a few explicitly marked endpoints, every request is authenticated with your institution's API Key and a **signature**. When you apply for the API Key, you register your **public key** with Webot and keep the private key yourself.

### 1. The Two Request Headers

| Header        | Description                                                                               |
| ------------- | ----------------------------------------------------------------------------------------- |
| `X-APIKEY`    | Your API Key, of the form `webot_xxxxxxxx`, used to look up your registered public key.   |
| `X-Signature` | The result of signing the canonical string below, **Base64-encoded (standard encoding)**. |

### 2. The Two Key Types and Their Signing Methods

The signing algorithm is determined by **the type of the public key you registered** — it is **not** chosen per request. Two key types are supported:

| Key type    | How to sign                             | Notes                                                                                                     |
| ----------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **RSA**     | **RSA-PSS** over the **SHA-256** digest | 2048–4096 bit keys. Recommended salt length is 32 (20 / 32 / 64 are all acceptable). **Not** PKCS#1 v1.5. |
| **Ed25519** | Sign the message bytes **directly**     | **Do not** pre-hash — EdDSA already includes SHA-512 internally.                                          |

* Public keys are registered in **PKIX/SPKI** form (`-----BEGIN PUBLIC KEY-----`). PKCS#1 "RSA PUBLIC KEY" and other algorithms (ECDSA, DSA, …) are not accepted.
* RSA reference implementation:
  * Go: `rsa.SignPSS(rand, priv, crypto.SHA256, sha256(msg), nil)`
  * OpenSSL: `-sigopt rsa_padding_mode:pss`

### 3. The Required Query Parameter `timestamp`

| Parameter   | Type    | Description                                                                                                                                                                                           |
| ----------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `timestamp` | integer | The current time, in **seconds** (Unix). Required on every signed request, and it **participates in the signature**. Must be within **±5 seconds** of server time, otherwise the request is rejected. |

> `timestamp` goes in the **query string**, **even for `POST`** (a POST's business data goes in the JSON body). There is **no** `client_id` / nonce parameter.

### 4. Building the Canonical String

Sign the following string:

```
{sub_path}:{sorted_query_string}:{request_body}:{timestamp}
```

| Segment               | How it is built                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `sub_path`            | The request path, **verbatim** (not URL-encoded), e.g. `/api/v2/institution/account/balances`.                                                                                                                                                                                                                                                                                                                                                                                       |
| `sorted_query_string` | Percent-encode each key and value **separately** with `encodeURIComponent` semantics — **do not** escape `A-Za-z0-9 - _ . ! ~ * ' ( )`, use **uppercase** hexadecimal, and encode spaces as `%20` (not the form-encoding `+`). Join each into `key=value`, then **sort those `key=value` strings as a whole**, and join them with `&`. **All query parameters participate** — `timestamp` plus the business parameters (e.g. `userId`); duplicate keys are kept and sorted by value. |
| `request_body`        | For `POST` with a JSON body: **the raw body verbatim** (do not re-serialize). For `GET`: the empty string (so `::` appears in the canonical string).                                                                                                                                                                                                                                                                                                                                 |
| `timestamp`           | The same second value, **appended again** at the end.                                                                                                                                                                                                                                                                                                                                                                                                                                |

**Official examples:**

```
GET  (no body):   /api/v2/institution/account/balances:timestamp=1785706000&userId=88001234::1785706000
POST (JSON body): /api/v2/institution/kyb/create:timestamp=1785706000:{"userId":"88001234"}:1785706000
```

Then `X-Signature = base64( sign( canonical_string ) )`, sent together with `X-APIKEY`.

> The most common integration error is getting a parameter's source wrong (query vs. body) — be sure to check against the table above.

### 5. Permission Scopes

Each API Key is granted one or more **scopes** at registration (default `read`). Every endpoint requires a specific scope; a request lacking the permission is rejected with `P_PAY_OPEN_API_PERMISSION_DENIED` and **does not enter the business logic**.

* **Read endpoints** (`GET` queries — balances, orders, records, requirements, status, lists) require **read**.
* **State-changing endpoints** (`POST` create/submit/update/delete — sub-account and KYB onboarding, payouts, on-chain withdrawals, conversions, address book and file writes) require the corresponding **write** scope.

Contact Webot to grant your key the scopes it needs; the scopes on a key **cannot be changed by the caller.**

### 6. `userId` Parameter Rules

Except for public/self endpoints (**create user**, **list users**, **`asset/currencies`**), **every endpoint requires `userId`** (the target sub-account's UUID):

| Request form                        | Where `userId` goes                                             |
| ----------------------------------- | --------------------------------------------------------------- |
| GET request                         | **Query string** (participates in the signature)                |
| POST request (JSON body)            | In the **JSON body**                                            |
| File upload (`multipart/form-data`) | **Query string** (the body is multipart, so it cannot go there) |

Obtain `userId` from the response of \[List Sub-Accounts] or \[Create Sub-Account].

### 7. Base Error Codes

| Error code                                       | Description                                                                                       |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `P_PAY_OPEN_API_INVALID_ARGUMENT`                | Request parameter missing or invalid.                                                             |
| `P_PAY_OPEN_API_UNAUTHENTICATED`                 | Authentication failed (missing key, bad signature, etc.). Returns HTTP `401`.                     |
| `P_PAY_OPEN_API_PERMISSION_DENIED`               | The API Key lacks permission for this endpoint.                                                   |
| `P_PAY_OPEN_API_NOT_FOUND`                       | Resource does not exist, or does not belong to this user.                                         |
| `P_PAY_OPEN_API_ALREADY_EXISTS`                  | Resource already exists (e.g. the address is already in the address book).                        |
| `P_PAY_OPEN_API_SERVICE_UNAVAILABLE`             | A dependency is temporarily unavailable; retry later.                                             |
| `P_PAY_OPEN_API_TIMEOUT`                         | Request timed out; retry later.                                                                   |
| `P_PAY_OPEN_API_INTERNAL_ERROR`                  | Internal error.                                                                                   |
| `P_PAY_OPEN_API_OPERATION_NOT_SUPPORTED`         | The channel does not support this operation. Do not retry.                                        |
| `P_PAY_OPEN_API_CHANNEL_NOT_SUPPORTED_IN_REGION` | The requested `channel` is not available for your onboarding. Do not retry with the same channel. |
| `P_PAY_OPEN_API_INTERNAL_KYB_NOT_APPROVED`       | Platform KYB is not yet `APPROVED` — the `wire/*` gate.                                           |

***

## Appendix: Platform KYB Field Reference

> The platform KYB field list is **fixed and documented**, and applies to companies registered in **Hong Kong (HK)** or **other jurisdictions**. The following are the canonical fields and documents accepted by `POST /kyb/create`. The full field schema is also in `openapi_institution_v2.yaml`.

**Requirement markers:** **M** = Mandatory (missing it fails validation with `P_PAY_OPEN_API_INVALID_ARGUMENT`); **C** = Conditional (required when the trigger condition in the description is met); **O** = Optional (providing it speeds up review; missing it does not block). The `HK`/`Other` columns give the marker for each jurisdiction (`subject.country`), and `N/A` means the jurisdiction ignores the field. Files are submitted via `documents[]`, using the document's canonical key as `purpose` and the `fileId` as the value.

### Subject Fields (`subject.*`)

| Key                                                        |  HK | Other | Rule                                                                                                                                                                                                                                    |
| ---------------------------------------------------------- | :-: | :---: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `subject.country`                                          |  M  |   M   | Registration jurisdiction, ISO alpha-2 country code, e.g. `HK`.                                                                                                                                                                         |
| `subject.legalNameEn`                                      |  M  |   M   | Legal English name, ≤200, must match the registration document exactly (including `Limited`/`Ltd.`/`Inc.` suffixes). Non-Latin names require an official English translation.                                                           |
| `subject.legalNameCn`                                      |  O  |  N/A  | Legal Chinese name, ≤100. HK companies registered in Chinese should provide it; not applicable to Other.                                                                                                                                |
| `subject.registrationNo`                                   |  M  |   M   | HK: Business Registration number; Other: IRS EIN (format `XX-XXXXXXX`). ≤32.                                                                                                                                                            |
| `subject.registrationNoType`                               |  M  |   M   | `BRN` (HK) / `EIN` (Other), must match `subject.country`.                                                                                                                                                                               |
| `subject.businessType`                                     |  M  |   M   | Organizational form (see enum). Also determines which formation document Other requires.                                                                                                                                                |
| `subject.incorporationDate`                                |  M  |   M   | `yyyy-MM-dd`. Incorporation < 6 months may enter enhanced due diligence.                                                                                                                                                                |
| `subject.incorporationState`                               | N/A |   M   | Other state code (e.g. `DE`, `CA`), Other only.                                                                                                                                                                                         |
| `subject.email`                                            |  M  |   M   | Official business email, ≤128. Free email domains (gmail/qq/163…) trigger manual review.                                                                                                                                                |
| `subject.phone.countryCode`                                |  C  |   C   | International calling code without `+` (e.g. `852`, `1`). Required when a phone is provided.                                                                                                                                            |
| `subject.phone.number`                                     |  C  |   C   | Required when a phone is provided, ≤20.                                                                                                                                                                                                 |
| `subject.website`                                          |  O  |   O   | Must include the scheme (`https://`), ≤256. Recommended for e-commerce/platform businesses.                                                                                                                                             |
| `subject.registeredAddress.addressLine2`                   |  O  |   O   | Room/floor/unit, ≤200.                                                                                                                                                                                                                  |
| `subject.registeredAddress.city`                           |  M  |   M   | ≤100.                                                                                                                                                                                                                                   |
| `subject.registeredAddress.state`                          |  O  |   M   | 2-letter state code (e.g. `NY`). Required for Other; may be omitted for HK.                                                                                                                                                             |
| `subject.registeredAddress.postalCode`                     |  O  |   M   | Required for Other; HK has no postal code.                                                                                                                                                                                              |
| `subject.registeredAddress.countryCode`                    |  M  |   M   | ISO 3166-1 alpha-2.                                                                                                                                                                                                                     |
| `subject.operatingAddressSameAsRegistered`                 |  M  |   M   | `true`/`false`. When `true`, omit `operatingAddress`.                                                                                                                                                                                   |
| `subject.operatingAddress.*`                               |  C  |   C   | Sub-fields same as `registeredAddress`. Required when the above is `false`.                                                                                                                                                             |
| `subject.businessDescription`                              |  M  |   M   | A concrete description of the products/services, ≤500. Vague terms ("trading", "consulting") will be asked to elaborate.                                                                                                                |
| `subject.accountPurpose.cryptoTrading` etc.                |  O  |   O   | Each `accountPurpose.*` is `true`/`false`: `cryptoTrading`/`fiatDeposit`/`fiatWithdrawal`/`cardIssuing`/`crossBorderPayment`/`fxConversion`/`payroll`/`other`. At least one should be `true`; explain `other` in `businessDescription`. |
| `subject.monthlyDepositLimit.amount`                       |  M  |   M   | Decimal string, ≤2 decimal places, USD recommended.                                                                                                                                                                                     |
| `subject.monthlyDepositLimit.currency`                     |  M  |   M   | ISO 4217.                                                                                                                                                                                                                               |
| `subject.monthlyWithdrawalLimit.amount`                    |  M  |   M   | Decimal string, ≤2 decimal places.                                                                                                                                                                                                      |
| `subject.monthlyWithdrawalLimit.currency`                  |  M  |   M   | ISO 4217.                                                                                                                                                                                                                               |
| `subject.pepDeclaration.hasPepRelation`                    |  M  |   M   | `true`/`false`. Whether a director/shareholder/UBO (or a close relative) is a politically exposed person.                                                                                                                               |
| `subject.pepDeclaration.description`                       |  C  |   C   | Required when `hasPepRelation = true`, ≤500 (name, position, tenure).                                                                                                                                                                   |
| `subject.ownershipDeclaration.hasShareholderOver25Percent` |  C  |   C   | `true`/`false`. Required when no ownership-structure proof document is submitted. When `true`, `representatives[]` must include at least one `role.responsibility = ULTIMATE_BENEFICIAL_OWNER`.                                         |
| `subject.ownershipDeclaration.hasNomineeShareholder`       |  O  |   O   | `true`/`false`. When `true`, the ultimate beneficiary must be disclosed.                                                                                                                                                                |
| `subject.highRiskCountryExposure.involved`                 |  O  |   O   | `true`/`false`. Whether the business involves FATF high-risk jurisdictions.                                                                                                                                                             |
| `subject.highRiskCountryExposure.description`              |  C  |   C   | Required when `involved = true`, ≤500.                                                                                                                                                                                                  |
| `subject.termsAgreed`                                      |  M  |   M   | Must be `true`, otherwise the application is rejected.                                                                                                                                                                                  |
| `subject.dataUsageAgreed`                                  |  M  |   M   | Must be `true` (authorizes third-party data verification).                                                                                                                                                                              |
| `subject.serviceAgreementType`                             |  M  |   M   | `FULL`/`RECIPIENT` (see enum).                                                                                                                                                                                                          |
| `subject.signerPersonRefId`                                |  M  |   M   | Must equal the `representativeRef` of the person designated as the authorized signer.                                                                                                                                                   |
| `subject.agreedAt`                                         |  M  |   M   | ISO 8601 (e.g. `2026-08-25T10:12:33Z`).                                                                                                                                                                                                 |
| `subject.deviceData.ipAddress`                             |  M  |   M   | The signer's public IP at the time of consent (IPv6-compatible), ≤45.                                                                                                                                                                   |
| `subject.deviceData.userAgent`                             |  M  |   M   | The signer's User-Agent, ≤512.                                                                                                                                                                                                          |

### Subject Documents (`subject.*`, value is a `fileId`)

| Key                                                                                            |  HK | Other | Description                                                                                                     |
| ---------------------------------------------------------------------------------------------- | :-: | :---: | --------------------------------------------------------------------------------------------------------------- |
| `subject.businessRegistrationCertificate`                                                      |  M  |  N/A  | Business Registration certificate (BR).                                                                         |
| `subject.businessFormation`                                                                    |  M  |   M   | Certificate of incorporation (HK CI / Other Certificate of Incorporation).                                      |
| `subject.incorporationFormNnc1`                                                                |  C  |  N/A  | Incorporation form NNC1. **Note 1**.                                                                            |
| `subject.annualReturnNar1`                                                                     |  C  |  N/A  | Annual return NAR1. **Note 1**.                                                                                 |
| `subject.einConfirmationLetter`                                                                | N/A |   M   | IRS EIN confirmation letter (CP575 / 147C).                                                                     |
| `subject.bylaws`                                                                               | N/A |   C   | Bylaws — when `businessType` is a corporation subtype (`B_/C_/CLOSE_/S_CORPORATION`). **Note 4**.               |
| `subject.operatingAgreement`                                                                   | N/A |   C   | Bylaws — when `businessType = LLC`, or the consolidated fallback when businessType is not provided. **Note 4**. |
| `subject.partnershipAgreement`                                                                 | N/A |   C   | Bylaws — when `businessType = LLP`/`LP`/`GENERAL_PARTNERSHIP`. **Note 4**.                                      |
| `subject.registerOfDirectors`                                                                  |  C  |   C   | Register of directors. **Note 2**.                                                                              |
| `subject.ownershipProof`                                                                       |  C  |   C   | Register of shareholders. **Note 2**.                                                                           |
| `subject.shareholdingStructureChart`                                                           |  C  |   C   | Shareholding structure chart. **Note 2, Note 3**.                                                               |
| `subject.certificateOfGoodStanding`                                                            | N/A |   O   | Certificate of good standing.                                                                                   |
| `subject.financialStatements`                                                                  |  O  |   O   | Financial statements.                                                                                           |
| `subject.authorizationLetter`                                                                  |  O  |   O   | Authorization letter.                                                                                           |
| `subject.sourceOfFundsProof`                                                                   |  M  |   M   | Source-of-funds proof — see **Note 5**.                                                                         |
| `subject.supportiveOther`                                                                      |  O  |   O   | Other supporting materials.                                                                                     |
| `subject.bankStatement` / `utilityBill` / `leaseAgreement` / `taxNotice` / `addressProofOther` |  O  |   O   | Address proof (bank statement / utility bill / lease agreement / tax notice / other).                           |

**Conditional rules:**

* **Note 1 (HK formation documents):** Submit at least one of `incorporationFormNnc1` / `annualReturnNar1`. For companies incorporated over a year ago, prefer `annualReturnNar1` (most recent directors/shareholders/address).
* **Note 2 (ownership and directors):** Submit at least one of `registerOfDirectors` / `ownershipProof` / `shareholdingStructureChart` that fully shows directors and ownership. If already reflected by NNC1/NAR1 (HK) or the formation documents (Other), it may be omitted, in which case `subject.ownershipDeclaration.*` may also be omitted.
* **Note 3:** If none of the above can show the complete ownership chain (e.g. multi-layer holdings), the supplement stage will request a `shareholdingStructureChart`.
* **Note 4 (Other formation documents):** Submit the matching formation document per `businessType` — corporation subtype → `bylaws`; `LLC` → `operatingAgreement`; `LLP`/`LP`/`GENERAL_PARTNERSHIP` → `partnershipAgreement`. Submit only one, matching the actual organizational form. (When the type is unclear, `operatingAgreement` serves as the fallback.)
* **Note 5 (source of funds):** Mandatory. Acceptable: company bank statements for the last 6 months, audited financial statements, key trade contracts + invoices, proof of capital contribution, investment agreements + receipts, etc. Multiple files are allowed (at least one). An account-balance screenshot alone is insufficient — it must show the chain by which the funds were formed.
* **Address proof:** Required when `operatingAddressSameAsRegistered = false`, or when the submitted registration documents do not state the address. Provide it using the `subject.*` address-proof keys listed above; it must have been issued within the last 3 months.

### Representative Fields (`representative.*`)

One group per person, distinguished by the **`representativeRef` attribute** of the `representatives[]` entry (**not** a key inside `fields[]`).

| Key                                               |  HK | Other | Rule                                                                                                                                  |
| ------------------------------------------------- | :-: | :---: | ------------------------------------------------------------------------------------------------------------------------------------- |
| `representativeRef` (attribute, not a fields key) |  M  |   M   | A stable per-person id in your system, kept unchanged across supplements. `subject.signerPersonRefId` references this value.          |
| `representative.role.responsibility`              |  O  |   O   | **Single-value** enum: `ULTIMATE_BENEFICIAL_OWNER`/`AUTHORIZED_REPRESENTATIVE`/`DIRECTOR`.                                            |
| `representative.firstName`                        |  M  |   M   | English first name, ≤100, must match the ID document exactly.                                                                         |
| `representative.middleName`                       |  O  |   O   | English middle name, ≤100; fill in if the ID document has one.                                                                        |
| `representative.lastName`                         |  M  |   M   | English last name, ≤100, must match the ID document.                                                                                  |
| `representative.fullNameCn`                       |  O  |  N/A  | Chinese name, ≤50. HK: fill in if the ID document has a Chinese name.                                                                 |
| `representative.jobTitle`                         |  M  |   M   | Position (e.g. Director, CEO), ≤100.                                                                                                  |
| `representative.birthDate`                        |  M  |   M   | `yyyy-MM-dd`, must match the ID document, must be ≥ 18 years old.                                                                     |
| `representative.nationality`                      |  M  |   M   | ISO 3166-1 alpha-2.                                                                                                                   |
| `representative.ownershipPercentage`              |  C  |   C   | Required when `role.responsibility = ULTIMATE_BENEFICIAL_OWNER`. `0.01`–`100`, ≤2 decimal places; the actual look-through percentage. |
| `representative.residentialAddress.addressLine1`  |  M  |   M   | Actual residential address (not temporary), ≤200.                                                                                     |
| `representative.residentialAddress.addressLine2`  |  O  |   O   | ≤200.                                                                                                                                 |
| `representative.residentialAddress.city`          |  M  |   M   | ≤100.                                                                                                                                 |
| `representative.residentialAddress.state`         |  O  |   M   | Required for Other (2-letter state code).                                                                                             |
| `representative.residentialAddress.postalCode`    |  O  |   M   | Required for Other.                                                                                                                   |
| `representative.residentialAddress.countryCode`   |  M  |   M   | ISO 3166-1 alpha-2.                                                                                                                   |
| `representative.email`                            |  M  |   M   | Required for each person, ≤128 (used for verification-code delivery).                                                                 |
| `representative.phone.countryCode` / `.number`    |  O  |   O   | Recommended for the primary contact.                                                                                                  |
| `representative.identityDocument.ssn`             | N/A |   M   | Other: each person's individual tax number (SSN/ITIN); not applicable to HK.                                                          |
| `representative.identityDocument.idType`          |  M  |   M   | See enum.                                                                                                                             |
| `representative.identityDocument.idNumber`        |  M  |   M   | ≤64.                                                                                                                                  |
| `representative.identityDocument.issuingCountry`  |  M  |   M   | ISO 3166-1 alpha-2.                                                                                                                   |
| `representative.identityDocument.issueDate`       |  O  |   O   | `yyyy-MM-dd`.                                                                                                                         |
| `representative.identityDocument.expiryDate`      |  M  |   M   | `yyyy-MM-dd`. Use `9999-12-31` for permanent documents; expired documents fail validation.                                            |
| `representative.ownershipAttestedAt`              |  C  |   C   | ISO 8601. Required when `subject.ownershipDeclaration.hasShareholderOver25Percent` has a value.                                       |

### Representative Documents (`representative.*`, value is a `fileId`)

Route the ID document file by `identityDocument.idType` (see the idType routing note in the enum).

| Key                                                         | Req | Description                                                                                  |
| ----------------------------------------------------------- | :-: | -------------------------------------------------------------------------------------------- |
| `representative.passport`                                   |  C  | Passport photo page (front only) — when `idType = PASSPORT`.                                 |
| `representative.idCardFront` / `idCardBack`                 |  C  | Both front and back of card-type documents are required — see idType routing.                |
| `representative.driversLicenseFront` / `driversLicenseBack` |  C  | Both front and back required when `idType = DRIVERS_LICENSE`.                                |
| `representative.taxIdDocument`                              |  C  | Tax-number proof (SSN/ITIN), provided by Other persons together with `identityDocument.ssn`. |
| `representative.proofOfAddress`                             |  C  | Required when the residential address differs from the address on the ID document.           |
| `representative.photoHoldingId`                             |  O  | Requested by risk control in cases of suspected fraud.                                       |
| `representative.liveSelfie`                                 |  O  | Same as above.                                                                               |
| `representative.appointmentDocument`                        |  O  | Appointment/authorization document.                                                          |
| `representative.nameChangeCertificate`                      |  C  | Required when the name on the ID document differs from the declared name.                    |
| `representative.supportiveOther`                            |  O  | Other supporting materials.                                                                  |

### Enums

* **`subject.country`**: `HK`, `Other`.
* **`subject.registrationNoType`**: `BRN` (HK), `EIN` (Other).
* **`subject.businessType`** (single list, not split by jurisdiction): `B_CORPORATION`, `C_CORPORATION`, `CLOSE_CORPORATION`, `S_CORPORATION`, `LLC`, `LLP`, `LP`, `GENERAL_PARTNERSHIP`, `SOLE_PROPRIETOR`, `TRUST`, `COOPERATIVE`, `NONPROFIT_CORPORATION`, `OTHER`. (`OTHER` requires a description; the subset actually allowed per channel/country is returned by `requirements`.)
* **`subject.serviceAgreementType`**: `FULL` (direct service relationship), `RECIPIENT` (recipient only, no direct relationship).
* **`representative.role.responsibility`** (single value): `ULTIMATE_BENEFICIAL_OWNER`, `AUTHORIZED_REPRESENTATIVE`, `DIRECTOR`.
* **`representative.identityDocument.idType`**: `PASSPORT`, `DRIVERS_LICENSE`, `NATIONAL_ID`, `STATE_OR_PROVINCIAL_ID`, `PERMANENT_RESIDENCY_ID`, `MATRICULATE_ID`, `MILITARY_ID`, `VISA`.
* **ID document files routed by idType:** `PASSPORT` → `passport`; `DRIVERS_LICENSE` → `driversLicenseFront` + `driversLicenseBack`; all other government IDs (`NATIONAL_ID`/`STATE_OR_PROVINCIAL_ID`/`PERMANENT_RESIDENCY_ID`/`MATRICULATE_ID`/`MILITARY_ID`/`VISA`) → `idCardFront` + `idCardBack`.
* **Address proof** (which `subject.*` document key to use): bank statement → `bankStatement`; utility bill → `utilityBill`; lease agreement → `leaseAgreement`; tax notice → `taxNotice`; other → `addressProofOther`.

### File Limits

| Limit                          | Value                                                      |
| ------------------------------ | ---------------------------------------------------------- |
| Max files per submission       | 30                                                         |
| Max bytes per file             | 12,582,912 (12 MB)                                         |
| Max total bytes per submission | 104,857,600 (100 MB)                                       |
| Allowed content types          | `application/pdf`, `image/jpeg`, `image/png`, `image/heic` |

### Role Integrity and Cross-Field Rules

If any of the following is not satisfied, the request is rejected with `P_PAY_OPEN_API_INVALID_ARGUMENT`:

* **Registration type matches jurisdiction:** `HK` ⇒ `registrationNoType = BRN`; `Other` ⇒ `EIN`.
* **Other state required:** `Other` requires `subject.incorporationState`.
* **Operating address required:** when `operatingAddressSameAsRegistered = false`, `operatingAddress` must be provided.
* **Every representative has an `email`.**
* **Beneficial-owner ownership:** when `hasShareholderOver25Percent = true` (or the ownership documents show a holder ≥25%), at least one representative has `role.responsibility = ULTIMATE_BENEFICIAL_OWNER`; each such role's `ownershipPercentage` is in `(0, 100]`, and the total is ≤ 100.
* **ID document not expired**; card-type documents and `DRIVERS_LICENSE` must have a **back** file.
* **Age ≥ 18** (determined from `birthDate`).
* **Other formation document matches `businessType`** (see Note 4).
* **Each Other representative has an individual tax number** (`identityDocument.ssn`).
* **Required company documents are complete** (per the matrix); source-of-funds proof is complete; ownership information is resolvable (via ownership documents or `ownershipDeclaration.*`).
* **Consent items accepted:** both `termsAgreed` and `dataUsageAgreed` are `true`.
