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

# Upgrade a business customer from MINIMAL to FULL KYB

> Promote a `kyb_scope=MINIMAL` business customer to `kyb_scope=FULL` by attaching beneficial-owner data. Identity columns (`registration_number`, `incorporation_country`) are typically already on file from MINIMAL onboarding.

**Eligibility checks (return `422` on failure):**

- Customer exists and belongs to your platform.
- Customer is `type=business`.
- Customer is currently `kyb_scope=MINIMAL` (or has no scope set, treated as Minimal). A FULL customer cannot be "upgraded" again.
- Customer is not `PROCESSING` (rejected with `400` — wait for the customer-level webhook before retrying).

**Validation (return `422` on failure):**

- At least one owner.
- Ownership percentages sum to exactly 100.
- Per-owner identity invariants (DOB ≥ 18, expiry future, valid ISO codes where supplied).
- Owner emails unique within the customer.
- If you send `registration_number` / `incorporation_country` overrides, they must satisfy the same rules as create.
- If you send `country` to update jurisdiction, the resulting `country` and `incorporation_country` must match.

**On success:**

1. New owners are saved on the customer.
2. `kyb_scope` flips to `FULL`.
3. If the customer was `VERIFIED`, `verification_status` flips back to `PENDING` (re-onboarding under stricter rules). Existing NGN VBAs are unaffected.
4. An audit comment is recorded.
5. A `customer.status_changed` webhook fires for the verification-status transition (if any).

**Important:** This endpoint does NOT run the Standard readiness checks. Owner ID files are not part of this payload — upload them per-owner via `POST /api/external/customer/{id}/owner/{owner}/files` after the upgrade succeeds. Then call `POST /api/external/customer/{id}/submit` to re-verify the customer under the Standard checks.

Required scope: `customer:write`.



## OpenAPI

````yaml /api-reference/customer/upgrade-kyb-scope-openapi.json post /api/external/customer/{customer}/upgrade-kyb-scope
openapi: 3.1.0
info:
  title: Blaaiz Platform API - Upgrade Customer KYB Scope (MINIMAL → FULL)
  version: 1.0.0
  description: >-
    Promote a `kyb_scope=MINIMAL` business customer to `kyb_scope=FULL` so they
    qualify for non-NGN Virtual Bank Accounts (USD / GBP / EUR) and the broader
    Standard KYB feature set. The upgrade attaches beneficial owners and flips
    the scope.


    **One-way only.** A FULL customer cannot be downgraded to MINIMAL. There is
    no admin path either — once compliance has accepted full data, it stays on
    file.


    **This endpoint saves the new data and flips `kyb_scope` to `FULL` — it does
    not run the Standard readiness checks.** Those checks (owner ID files
    uploaded, ownership-percentage rollup to 100, formation document on file)
    run at `/submit` time. Splitting the steps lets you upload owner ID files
    after the owners exist as records — running the checks here would force you
    to upload all owner files before owners even existed.


    **Sequence after a successful upgrade:**


    1. Upload owner ID files via `POST
    /api/external/customer/{id}/owner/{owner}/files` (per owner, per slot).

    2. Call `POST /api/external/customer/{id}/submit` to re-route the customer
    through review under the Standard checks.


    If the customer was `VERIFIED` at the time of the upgrade, this endpoint
    also flips them back to `PENDING` (an upgrade is a re-onboarding under
    stricter rules). Existing NGN VBAs are unaffected. The customer is editable
    again.


    **Identity columns are typically not in the payload.** `registration_number`
    and `incorporation_country` are required at MINIMAL onboarding (canonical
    for both scopes), so they're already on the customer when you upgrade. The
    typical upgrade payload is just `owners` plus optional KYB augmentations
    (`industry_type`, `business_type`, etc.). You can send `registration_number`
    / `incorporation_country` to override values on file, but that's rarely
    needed.
servers:
  - url: https://api-prod.blaaiz.com
  - url: https://api-dev.blaaiz.com
security: []
tags:
  - name: Customer
paths:
  /api/external/customer/{customer}/upgrade-kyb-scope:
    post:
      tags:
        - Customer
      summary: Upgrade a business customer from MINIMAL to FULL KYB
      description: >-
        Promote a `kyb_scope=MINIMAL` business customer to `kyb_scope=FULL` by
        attaching beneficial-owner data. Identity columns
        (`registration_number`, `incorporation_country`) are typically already
        on file from MINIMAL onboarding.


        **Eligibility checks (return `422` on failure):**


        - Customer exists and belongs to your platform.

        - Customer is `type=business`.

        - Customer is currently `kyb_scope=MINIMAL` (or has no scope set,
        treated as Minimal). A FULL customer cannot be "upgraded" again.

        - Customer is not `PROCESSING` (rejected with `400` — wait for the
        customer-level webhook before retrying).


        **Validation (return `422` on failure):**


        - At least one owner.

        - Ownership percentages sum to exactly 100.

        - Per-owner identity invariants (DOB ≥ 18, expiry future, valid ISO
        codes where supplied).

        - Owner emails unique within the customer.

        - If you send `registration_number` / `incorporation_country` overrides,
        they must satisfy the same rules as create.

        - If you send `country` to update jurisdiction, the resulting `country`
        and `incorporation_country` must match.


        **On success:**


        1. New owners are saved on the customer.

        2. `kyb_scope` flips to `FULL`.

        3. If the customer was `VERIFIED`, `verification_status` flips back to
        `PENDING` (re-onboarding under stricter rules). Existing NGN VBAs are
        unaffected.

        4. An audit comment is recorded.

        5. A `customer.status_changed` webhook fires for the verification-status
        transition (if any).


        **Important:** This endpoint does NOT run the Standard readiness checks.
        Owner ID files are not part of this payload — upload them per-owner via
        `POST /api/external/customer/{id}/owner/{owner}/files` after the upgrade
        succeeds. Then call `POST /api/external/customer/{id}/submit` to
        re-verify the customer under the Standard checks.


        Required scope: `customer:write`.
      parameters:
        - name: customer
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpgradeKybScopeRequest'
            example:
              trading_name: Acme
              business_type: llc
              incorporation_date: '2019-03-12'
              industry_type: Software & SaaS
              business_description: B2B SaaS platform.
              website: https://acme.example.com
              source_of_funds: business_revenue
              estimated_annual_revenue: '1000000_4999999'
              expected_monthly_payments: 250
              account_purpose: send_receive_funds_related_parties
              owners:
                - first_name: Jane
                  last_name: Doe
                  email: jane@acme.example.com
                  date_of_birth: '1985-04-22'
                  nationality: NG
                  title: CEO
                  ownership_percentage: 60
                  has_control: true
                  is_signer: true
                  is_beneficial_owner: true
                  id_document_type: passport
                  id_document_number: A12345678
                  id_document_country: NG
                  id_expiry_date: '2030-01-15'
                  is_pep: false
                - first_name: John
                  last_name: Smith
                  ownership_percentage: 40
                  is_beneficial_owner: true
      responses:
        '200':
          description: >-
            Customer upgraded. `kyb_scope` is now `FULL`. Owner ID files still
            need to be uploaded; then call `/submit` to re-verify under the FULL
            floor.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpgradeKybScopeResponse'
              example:
                message: >-
                  Customer KYB scope upgraded to FULL. Upload owner ID files and
                  call /submit to re-verify.
                data:
                  id: 019a6da3-4a9a-7033-81b9-12489eff13ee
                  type: business
                  kyb_scope: FULL
                  verification_status: PENDING
                  business_name: Acme Nigeria Limited
                  registration_number: RC123456
                  incorporation_country: NG
                  country: NG
                  owners:
                    - id: 019a6e22-8b4c-7c8d-9d12-c0c1ab3f1a90
                      first_name: Jane
                      last_name: Doe
                      ownership_percentage: 60
                      status: PENDING
                    - id: 019a6e22-8b4c-7c8d-9d12-d1d2bc4e2b91
                      first_name: John
                      last_name: Smith
                      ownership_percentage: 40
                      status: PENDING
        '400':
          description: Customer is currently under review.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                status: false
                message: Customer is under review and cannot be upgraded right now.
        '401':
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                status: false
                message: Invalid or missing access token
        '404':
          description: Customer not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                status: false
                message: Customer not found
        '422':
          description: >-
            Eligibility or validation failure. The message names the specific
            problem.
          content:
            application/json:
              examples:
                wrong_type:
                  value:
                    message: Only business customers can be upgraded.
                already_full:
                  value:
                    message: Customer is already at kyb_scope=FULL.
                country_mismatch:
                  value:
                    message: country must equal incorporation_country.
                duplicate_business_identity:
                  summary: Upgrade would duplicate another customer's business identity
                  value:
                    message: >-
                      Another business customer in this platform already has
                      this registration_number + incorporation_country
                      combination.
                missing_owners:
                  value:
                    message: owners is required and must contain at least one entry.
                ownership_sum_wrong:
                  value:
                    message: owners.ownership_percentage must sum to 100; got 90.
                duplicate_owner_emails:
                  value:
                    message: Owner emails must be unique within the customer.
      security:
        - oauth2ClientCredentials:
            - customer:write
components:
  schemas:
    UpgradeKybScopeRequest:
      type: object
      required:
        - owners
      properties:
        registration_number:
          type: string
          description: >-
            Optional override. The business registration / company number is
            already on the customer from MINIMAL onboarding (canonical for both
            scopes), so you typically don't send this. Send it only to update
            the value during the upgrade. Subject to the standard uniqueness
            rule on `(business_id, registration_number, incorporation_country)`.
        incorporation_country:
          type: string
          description: >-
            Optional override. Already on the customer from Minimal onboarding.
            Send only if you want to update the value during the upgrade. Must
            match the customer's `country` after your changes are applied — if
            they don't match, the request returns 422.
        country:
          type: string
          description: >-
            Optional. Update the registered-address country alongside
            `incorporation_country` when correcting jurisdiction during the
            upgrade. The resulting `country` and `incorporation_country` must
            match.
        owners:
          type: array
          minItems: 1
          maxItems: 5
          description: >-
            Beneficial owners, signatories, and directors. **Required, at least
            one owner.** The sum of every owner's `ownership_percentage` must
            equal exactly `100` (validated at this endpoint). Per-owner ID files
            are uploaded after the upgrade succeeds, via the dedicated
            owner-file endpoints — they're not part of this payload.
          items:
            type: object
            required:
              - first_name
              - last_name
              - ownership_percentage
            properties:
              first_name:
                type: string
                maxLength: 255
              last_name:
                type: string
                maxLength: 255
              email:
                type: string
                format: email
              phone:
                type: string
              date_of_birth:
                type: string
                format: date
                description: Must be at least 18 years ago.
              nationality:
                type: string
                description: ISO alpha-2.
              title:
                type: string
                description: e.g. 'CEO', 'Director'.
              ownership_percentage:
                type: number
                minimum: 0
                maximum: 100
              has_control:
                type: boolean
              is_signer:
                type: boolean
              is_beneficial_owner:
                type: boolean
              street:
                type: string
                minLength: 9
                maxLength: 255
              city:
                type: string
              state:
                type: string
              zip_code:
                type: string
              country:
                type: string
                description: ISO alpha-2.
              id_document_type:
                type: string
                enum:
                  - passport
                  - drivers_license
                  - id_card
                  - resident_permit
              id_document_number:
                type: string
              id_document_country:
                type: string
                description: ISO alpha-2.
              id_expiry_date:
                type: string
                format: date
                description: Must be in the future.
              tin:
                type: string
                minLength: 5
                maxLength: 50
              is_pep:
                type: boolean
              usa_tax_residency:
                type: boolean
        trading_name:
          type: string
          description: Optional augmentation. Trading or 'doing business as' name.
        business_type:
          type: string
          enum:
            - corporation
            - government_entity
            - llc
            - non_profit
            - other
            - partnership
            - sole_proprietorship
          description: Optional augmentation. Legal entity type.
        incorporation_date:
          type: string
          format: date
          description: Optional augmentation. Must be in the past.
        industry_type:
          type: string
          description: Optional augmentation.
        business_description:
          type: string
          description: Optional augmentation. Max 2000 chars.
        website:
          type: string
          format: uri
          description: Optional augmentation.
        source_of_funds:
          type: string
          enum:
            - business_revenue
            - business_loans
            - investment_income
            - third_party_funds
            - other
          description: Optional augmentation.
        estimated_annual_revenue:
          type: string
          enum:
            - '0_99999'
            - '100000_499999'
            - '500000_999999'
            - '1000000_4999999'
            - '5000000_24999999'
            - '25000000_99999999'
            - '100000000_249999999'
            - 250000000_plus
          description: Optional augmentation. Estimated annual revenue band, in USD.
        expected_monthly_payments:
          type: integer
          description: Optional augmentation.
        account_purpose:
          type: string
          enum:
            - receive_payments_for_goods_and_services
            - send_payments_for_goods_and_services
            - send_receive_funds_related_parties
            - other
          description: Optional augmentation.
        operating_street:
          type: string
        operating_city:
          type: string
        operating_state:
          type: string
        operating_zip_code:
          type: string
          description: Validated against `operating_country`.
        operating_country:
          type: string
          description: >-
            ISO alpha-2. Required whenever any other `operating_*` field is
            supplied.
    UpgradeKybScopeResponse:
      type: object
      properties:
        message:
          type: string
        data:
          $ref: '#/components/schemas/Customer'
      required:
        - message
        - data
    Error:
      type: object
      properties:
        status:
          type: boolean
        message:
          type: string
      required:
        - status
        - message
    Customer:
      type: object
      description: >-
        Updated customer record after the upgrade. `kyb_scope` is now `FULL`.
        `verification_status` is `PENDING` (or whatever the customer was, except
        `VERIFIED` which transitions to `PENDING` automatically). Same shape as
        `GET /customer/{id}`.
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
            - business
        kyb_scope:
          type: string
          enum:
            - FULL
        verification_status:
          type: string
          enum:
            - PENDING
            - REJECTED
            - PROCESSING
        business_name:
          type: string
        registration_number:
          type: string
        incorporation_country:
          type: string
        country:
          type: string
        owners:
          type: array
          items:
            type: object
  securitySchemes:
    oauth2ClientCredentials:
      type: oauth2
      description: >-
        Use your OAuth client credentials to obtain a short-lived Bearer token
        from POST /oauth/token.
      flows:
        clientCredentials:
          tokenUrl: /oauth/token
          scopes:
            customer:write: Create and update customers and KYC data.

````