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

# Onboard Merchant

> Onboard a merchant to accept payments through Modulus Labs

## Overview

This endpoint onboards a merchant to accept various payment methods. Before calling this API, you must upload all required documents using the File Upload API based on the business type.

<Warning>
  **Document verification required:** The API verifies that all required documents have been uploaded for the selected business type. If documents are missing, the request will fail.
</Warning>

## Prerequisites

<Steps>
  <Step title="Create Account">
    Create a merchant account using the [Create Account API](/api-reference/onboarding/create-account) to obtain an Account ID
  </Step>

  <Step title="Upload Documents">
    Upload required KYC documents via [File Upload API](/api-reference/onboarding/upload-onboarding-files) (requirements vary by business type)
  </Step>

  <Step title="Generate JWT Token">
    Create a JWT Bearer Token with the Account ID and email (see [Authentication](/docs/onboarding/authentication))
  </Step>

  <Step title="Submit Onboarding">
    Call this endpoint with complete merchant details
  </Step>
</Steps>

## Authentication

This endpoint requires JWT Bearer Token authentication. See the [Authentication Guide](/docs/onboarding/authentication) for details.

```bash theme={null}
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

## Request Parameters

### Core Business Information

<ParamField body="businessType" type="string" required>
  Type of business entity. Determines which fields are required.

  **Values:** `STARTER`, `SOLE_PROPRIETOR`, `PARTNERSHIP`, `CORPORATION`

  See [Business Type Enum](/docs/onboarding/enums#business-type) for details.

  **Example:** `"STARTER"`
</ParamField>

<ParamField body="legalName" type="string" optional>
  Registered business or legal name

  **Length:** 1-500 characters

  **Required when:** `businessType` is `SOLE_PROPRIETOR`, `PARTNERSHIP`, or `CORPORATION`

  **Not required for:** `STARTER` business type

  **Example:** `"Starbucks Corporation"`
</ParamField>

<ParamField body="merchantName" type="string" required>
  Merchant display name (used in customer-facing transactions)

  **Length:** 1-500 characters

  **Example:** `"Starbucks"`
</ParamField>

<ParamField body="modeOfPayments" type="string[]" required>
  Payment methods to enable for the merchant

  **Values:** `TERMINAL`, `ECOM`, `PAYMENT_LINK`, `QRPH`, `PAY_WITH_MAYA`

  **Example:** `["ECOM", "QRPH"]`

  See [Mode of Payment Enum](/docs/onboarding/enums#mode-of-payment) for descriptions.
</ParamField>

<ParamField body="currency" type="string" required>
  Currency for customer transactions (not settlement)

  **Length:** Exactly 3 characters

  **Values:** `PHP`, `USD`

  **Default:** `PHP`

  **Example:** `"PHP"`
</ParamField>

<ParamField body="tin" type="string" required>
  Company Tax Identification Number

  **Length:** 9-12 digits

  **Format:** Numbers only (no dashes or spaces)

  **Example:** `"455691852"`
</ParamField>

<ParamField body="industry" type="string" required>
  Industry or sector of the business

  **Length:** 1-200 characters

  **Example:** `"Food and Beverage"`
</ParamField>

<ParamField body="serviceDescription" type="string" required>
  Brief description of business operations and services

  **Example:** `"Coffee shop specializing in specialty beverages and light meals"`
</ParamField>

<ParamField body="isBrickAndMortarStore" type="boolean" required>
  Indicates if business has a physical storefront

  **Example:** `true`
</ParamField>

### Incorporators

<ParamField body="incorporators" type="array" optional>
  Array of business incorporators/owners

  **Min:** 0, **Max:** 5

  **Requirements by business type:**

  * `STARTER`: Not required (don't include this field)
  * `SOLE_PROPRIETOR`: Exactly 1 incorporator required
  * `PARTNERSHIP`: Exactly 2 incorporators required
  * `CORPORATION`: Minimum 3 incorporators required

  <Expandable title="Incorporator Object Properties">
    <ParamField body="incorporators[].firstName" type="string" required>
      First name

      **Length:** 1-100 characters
    </ParamField>

    <ParamField body="incorporators[].middleName" type="string" optional>
      Middle name

      **Length:** 1-100 characters
    </ParamField>

    <ParamField body="incorporators[].lastName" type="string" required>
      Last name

      **Length:** 1-100 characters
    </ParamField>

    <ParamField body="incorporators[].emailAddress" type="string" required>
      Email address

      **Length:** 1-100 characters

      **Format:** Valid email
    </ParamField>

    <ParamField body="incorporators[].contactNumber" type="string" required>
      Contact number (digits only)

      **Length:** 1-30 characters

      **Format:** Numbers only
    </ParamField>

    <ParamField body="incorporators[].natureOfWork" type="string" required>
      Nature of work or occupation

      **Length:** 1-100 characters
    </ParamField>

    <ParamField body="incorporators[].sourceOfFunds" type="string" required>
      Source of funds

      **Values:** See [Source of Funds Enum](/docs/onboarding/enums#source-of-funds)
    </ParamField>

    <ParamField body="incorporators[].nationality" type="string" required>
      Nationality

      **Length:** 1-100 characters
    </ParamField>

    <ParamField body="incorporators[].dateOfBirth" type="string" required>
      Date of birth

      **Format:** ISO 8601 (e.g., `"1988-02-12T16:00:00.000Z"`)
    </ParamField>

    <ParamField body="incorporators[].address" type="object" required>
      Address object

      <Expandable title="Address Properties">
        <ParamField body="city" type="string" required>
          City (1-100 characters)
        </ParamField>

        <ParamField body="line1" type="string" required>
          Street address (1-100 characters)
        </ParamField>

        <ParamField body="buildingNameAndNumber" type="string" optional>
          Building name and number (1-200 characters)
        </ParamField>

        <ParamField body="state" type="string" required>
          State or province (1-64 characters)
        </ParamField>

        <ParamField body="postalCode" type="string" required>
          Postal code (4-10 digits, numbers only)
        </ParamField>

        <ParamField body="barangay" type="string" required>
          Barangay (1-200 characters)
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

### Signatories

<ParamField body="signatories" type="array" optional>
  Array of authorized signatories

  **Min:** 0, **Max:** 5

  <Expandable title="Signatory Object Properties">
    <ParamField body="signatories[].firstName" type="string" required>
      First name (1-100 characters)
    </ParamField>

    <ParamField body="signatories[].middleName" type="string" optional>
      Middle name (1-100 characters)
    </ParamField>

    <ParamField body="signatories[].lastName" type="string" required>
      Last name (1-100 characters)
    </ParamField>

    <ParamField body="signatories[].position" type="string" required>
      Position or title (1-100 characters)
    </ParamField>
  </Expandable>
</ParamField>

### Address

<ParamField body="address" type="object" required>
  Business address information

  <Expandable title="Office Address (Required)">
    <ParamField body="address.office" type="object" required>
      Main office location

      <ParamField body="city" type="string" required>
        City (1-100 characters)
      </ParamField>

      <ParamField body="line1" type="string" required>
        Street address (1-100 characters)
      </ParamField>

      <ParamField body="buildingNameAndNumber" type="string" optional>
        Building name and number (1-200 characters)
      </ParamField>

      <ParamField body="state" type="string" required>
        State or province (1-64 characters)
      </ParamField>

      <ParamField body="postalCode" type="string" required>
        Postal code (4-10 digits, numbers only)
      </ParamField>

      <ParamField body="contactNumber" type="string" required>
        Contact number (1-30 digits, numbers only)
      </ParamField>

      <ParamField body="barangay" type="string" required>
        Barangay (1-200 characters)
      </ParamField>
    </ParamField>
  </Expandable>

  <Expandable title="Registered Address (Conditional)">
    <ParamField body="address.registered" type="object" optional>
      Registered business address

      **Required for:** `SOLE_PROPRIETOR`, `PARTNERSHIP`, `CORPORATION`

      **Not required for:** `STARTER`

      <ParamField body="city" type="string" required>
        City (1-100 characters)
      </ParamField>

      <ParamField body="line1" type="string" required>
        Street address (1-100 characters)
      </ParamField>

      <ParamField body="buildingNameAndNumber" type="string" optional>
        Building name and number (1-200 characters)
      </ParamField>

      <ParamField body="state" type="string" required>
        State or province (1-64 characters)
      </ParamField>

      <ParamField body="postalCode" type="string" required>
        Postal code (4-10 digits)
      </ParamField>

      <ParamField body="contactNumber" type="string" required>
        Contact number (1-30 digits)
      </ParamField>

      <ParamField body="barangay" type="string" required>
        Barangay (1-200 characters)
      </ParamField>
    </ParamField>
  </Expandable>
</ParamField>

### Websites

<ParamField body="websites" type="object" optional>
  Business online presence

  <Expandable title="Website Properties">
    <ParamField body="businessWebsiteUrl" type="string" optional>
      Business website URL
    </ParamField>

    <ParamField body="isBusinessWebsiteUnderDevelopment" type="boolean" optional>
      Whether website is under development

      **Required if:** `businessWebsiteUrl` has a value
    </ParamField>

    <ParamField body="facebookUrl" type="string" optional>
      Facebook page URL
    </ParamField>

    <ParamField body="twitterUrl" type="string" optional>
      Twitter profile URL
    </ParamField>

    <ParamField body="instagramUrl" type="string" optional>
      Instagram profile URL
    </ParamField>

    <ParamField body="tiktokUrl" type="string" optional>
      TikTok profile URL
    </ParamField>
  </Expandable>
</ParamField>

### Representatives

<ParamField body="representatives" type="object" required>
  Authorized representatives

  <Expandable title="Authorized Representative (Required)">
    <ParamField body="representatives.authorized" type="object" required>
      Primary authorized representative

      <ParamField body="firstName" type="string" required>
        First name (1-100 characters)
      </ParamField>

      <ParamField body="middleName" type="string" optional>
        Middle name (1-100 characters)
      </ParamField>

      <ParamField body="lastName" type="string" required>
        Last name (1-100 characters)
      </ParamField>

      <ParamField body="emailAddress" type="string" required>
        Email address (1-100 characters, valid email format)
      </ParamField>

      <ParamField body="contactNumber" type="string" required>
        Contact number (1-30 digits, numbers only)
      </ParamField>

      <ParamField body="dateOfBirth" type="string" required>
        Date of birth (ISO 8601 format)
      </ParamField>

      <ParamField body="position" type="string" required>
        Position or title (1-100 characters)
      </ParamField>
    </ParamField>
  </Expandable>
</ParamField>

### Bank Accounts

<ParamField body="banks" type="array" required>
  Settlement bank accounts (at least one required)

  <Expandable title="Bank Account Properties">
    <ParamField body="banks[].accountDepositType" type="string" required>
      Type of settlement account

      **Values:** `BANK`, `GCASH`, `PAYMAYA`, `NO_ACCOUNT`

      See [Bank Account Deposit Type](/docs/onboarding/enums#bank-account-deposit-type)
    </ParamField>

    <ParamField body="banks[].accountType" type="string" optional>
      Account type

      **Values:** `SAVINGS`, `CHECKING_CURRENT`

      **Required when:** `accountDepositType` is `BANK`
    </ParamField>

    <ParamField body="banks[].bankName" type="string" optional>
      Bank name

      **Required when:** `accountDepositType` is `BANK`, `GCASH`, or `PAYMAYA`

      See [Bank Names Enum](/docs/onboarding/enums#bank-names) for valid values
    </ParamField>

    <ParamField body="banks[].accountName" type="string" optional>
      Account holder name (1-500 characters)

      **Required when:** `accountDepositType` is `BANK`, `GCASH`, or `PAYMAYA`
    </ParamField>

    <ParamField body="banks[].accountNumber" type="string" optional>
      Account or mobile number (1-500 characters)

      **Required when:** `accountDepositType` is `BANK`, `GCASH`, or `PAYMAYA`
    </ParamField>

    <ParamField body="banks[].currency" type="string" optional>
      Settlement currency (3 characters)

      **Required when:** `accountDepositType` is `BANK`, `GCASH`, or `PAYMAYA`

      **Values:** `PHP`, `USD`
    </ParamField>
  </Expandable>
</ParamField>

## Response

### Success Response

**Status Code:** `200 OK`

<ResponseField name="referenceNumber" type="string">
  Unique reference number for the onboarding record generated by Modulus Labs

  **Usage:** Keep this reference number for status queries and support requests

  **Example:** `"0a17c362-fe5c-4889-9cb5-47df71dac425"`
</ResponseField>

```json theme={null}
{
  "referenceNumber": "0a17c362-fe5c-4889-9cb5-47df71dac425"
}
```

<ResponseExample>
  ```json 200 Success theme={null}
  {
    "referenceNumber": "0a17c362-fe5c-4889-9cb5-47df71dac425"
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "code": "20000004",
    "error": "Please upload idsOfValidSignatories file/s before accessing this API.",
    "referenceNumber": "02383ca9-8d72-47e2-9dcb-23e535a96122"
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "code": "20000001",
    "error": "Invalid or expired JWT token",
    "referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
  }
  ```

  ```json 500 Internal Server Error theme={null}
  {
    "code": "10000001",
    "error": "An unexpected error occurred. Please try again later.",
    "referenceNumber": "097bf60a-bdab-40bf-b615-3c0eae693b86"
  }
  ```
</ResponseExample>

### Error Responses

See [Error Handling](/docs/errors) for complete error code reference.

<AccordionGroup>
  <Accordion title="400 Bad Request - Missing Documents">
    ```json theme={null}
    {
      "code": "20000004",
      "error": "Please upload idsOfValidSignatories file/s before accessing this API.",
      "referenceNumber": "02383ca9-8d72-47e2-9dcb-23e535a96122"
    }
    ```

    **Cause:** Required documents not uploaded for the business type

    **Solution:** Upload all required documents via File Upload API first
  </Accordion>

  <Accordion title="400 Bad Request - Validation Error">
    ```json theme={null}
    {
      "code": "20000005",
      "error": "Invalid incorporator count for CORPORATION business type. Minimum 3 required.",
      "referenceNumber": "abc123-def456-ghi789"
    }
    ```

    **Cause:** Request parameters don't meet validation rules

    **Solution:** Check business type requirements and parameter constraints
  </Accordion>

  <Accordion title="401 Unauthorized">
    ```json theme={null}
    {
      "code": "20000001",
      "error": "Invalid or expired JWT token",
      "referenceNumber": "xyz789-abc123-def456"
    }
    ```

    **Cause:** JWT token is invalid or expired

    **Solution:** Generate a new JWT token with correct credentials
  </Accordion>
</AccordionGroup>

<RequestExample>
  ```bash cURL theme={null}
  # First, generate JWT token (use script or online tool)
  TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

  curl -X POST https://kyc.sbx.moduluslabs.io/v2/onboard \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "businessType": "STARTER",
      "merchantName": "Starbucks",
      "modeOfPayments": ["ECOM"],
      "currency": "PHP",
      "tin": "455691852",
      "industry": "Food and Beverage",
      "serviceDescription": "Specialty coffee retailer",
      "isBrickAndMortarStore": true,
      "address": {
        "office": {
          "city": "Portland",
          "line1": "111 Clarence Basler Rd",
          "state": "Oregon(OR)",
          "postalCode": "97030",
          "contactNumber": "639865748123",
          "barangay": "Wilmington II"
        }
      },
      "representatives": {
        "authorized": {
          "firstName": "David",
          "lastName": "Haven",
          "emailAddress": "david@haven.com",
          "contactNumber": "09789364458",
          "dateOfBirth": "1988-02-12T16:00:00.000Z",
          "position": "Accountant"
        }
      },
      "banks": [
        {
          "accountDepositType": "GCASH",
          "bankName": "GCASH",
          "accountName": "Starbucks Corporation",
          "accountNumber": "09260000000"
        }
      ]
    }'
  ```

  ```javascript Node.js theme={null}
  const axios = require('axios');
  const jwt = require('jsonwebtoken');

  const SECRET_KEY = process.env.MODULUS_ONBOARDING_SECRET_KEY;

  // Generate JWT token
  function generateToken(accountId, email) {
    return jwt.sign({ sub: accountId, email }, SECRET_KEY, { algorithm: 'HS256' });
  }

  // Onboard merchant
  async function onboardMerchant() {
    const token = generateToken(1, 'merchant@gmail.com');

    const merchantData = {
      businessType: 'STARTER',
      legalName: 'Starbucks',
      merchantName: 'Starbucks',
      modeOfPayments: ['ECOM'],
      currency: 'PHP',
      tin: '455691852',
      industry: 'Food and Beverage',
      serviceDescription: 'Specialty coffee retailer',
      isBrickAndMortarStore: true,
      address: {
        office: {
          city: 'Portland',
          line1: '111 Clarence Basler Rd',
          buildingNameAndNumber: 'Gresham Hub',
          state: 'Oregon(OR)',
          postalCode: '97030',
          contactNumber: '639865748123',
          barangay: 'Wilmington II'
        }
      },
      websites: {
        businessWebsiteUrl: 'https://www.starbucks.com/',
        isBusinessWebsiteUnderDevelopment: false
      },
      representatives: {
        authorized: {
          firstName: 'David',
          middleName: 'Reese',
          lastName: 'Haven',
          emailAddress: 'david@haven.com',
          contactNumber: '09789364458',
          dateOfBirth: '1988-02-12T16:00:00.000Z',
          position: 'Accountant'
        }
      },
      banks: [
        {
          accountDepositType: 'GCASH',
          bankName: 'GCASH',
          accountName: 'Starbucks Corporation',
          accountNumber: '09260000000'
        },
        {
          accountDepositType: 'BANK',
          accountType: 'SAVINGS',
          bankName: 'ASIA_UNITED_BANK',
          accountName: 'Starbucks Corporation',
          accountNumber: '4716542700323890',
          currency: 'PHP'
        }
      ]
    };

    try {
      const response = await axios.post(
        'https://kyc.sbx.moduluslabs.io/v2/onboard',
        merchantData,
        {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
          }
        }
      );

      console.log(' Merchant onboarded successfully');
      console.log('Reference Number:', response.data.referenceNumber);
      return response.data;

    } catch (error) {
      console.error(' Onboarding failed:', error.response?.data || error.message);
      throw error;
    }
  }

  onboardMerchant();
  ```

  ```python Python theme={null}
  import os
  import requests
  import jwt

  SECRET_KEY = os.getenv('MODULUS_ONBOARDING_SECRET_KEY')

  def generate_token(account_id, email):
      """Generate JWT token"""
      return jwt.encode(
          {'sub': account_id, 'email': email},
          SECRET_KEY,
          algorithm='HS256'
      )

  def onboard_merchant():
      """Onboard a merchant"""
      token = generate_token(1, 'merchant@gmail.com')

      merchant_data = {
          'businessType': 'STARTER',
          'legalName': 'Starbucks',
          'merchantName': 'Starbucks',
          'modeOfPayments': ['ECOM'],
          'currency': 'PHP',
          'tin': '455691852',
          'industry': 'Food and Beverage',
          'serviceDescription': 'Specialty coffee retailer',
          'isBrickAndMortarStore': True,
          'address': {
              'office': {
                  'city': 'Portland',
                  'line1': '111 Clarence Basler Rd',
                  'buildingNameAndNumber': 'Gresham Hub',
                  'state': 'Oregon(OR)',
                  'postalCode': '97030',
                  'contactNumber': '639865748123',
                  'barangay': 'Wilmington II'
              }
          },
          'representatives': {
              'authorized': {
                  'firstName': 'David',
                  'middleName': 'Reese',
                  'lastName': 'Haven',
                  'emailAddress': 'david@haven.com',
                  'contactNumber': '09789364458',
                  'dateOfBirth': '1988-02-12T16:00:00.000Z',
                  'position': 'Accountant'
              }
          },
          'banks': [
              {
                  'accountDepositType': 'GCASH',
                  'bankName': 'GCASH',
                  'accountName': 'Starbucks Corporation',
                  'accountNumber': '09260000000'
              }
          ]
      }

      try:
          response = requests.post(
              'https://kyc.sbx.moduluslabs.io/v2/onboard',
              json=merchant_data,
              headers={
                  'Authorization': f'Bearer {token}',
                  'Content-Type': 'application/json'
              }
          )

          response.raise_for_status()
          print(' Merchant onboarded successfully')
          print(f"Reference Number: {response.json()['referenceNumber']}")
          return response.json()

      except requests.exceptions.HTTPError as e:
          print(f' Onboarding failed: {e.response.text}')
          raise

  if __name__ == '__main__':
      onboard_merchant()
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php'; // composer require firebase/php-jwt guzzlehttp/guzzle

  use Firebase\JWT\JWT;
  use GuzzleHttp\Client;
  use GuzzleHttp\Exception\RequestException;

  $secretKey = getenv('MODULUS_ONBOARDING_SECRET_KEY');

  /**
   * Generate JWT token
   */
  function generateToken($accountId, $email, $secretKey) {
      $payload = [
          'sub' => $accountId,
          'email' => $email
      ];
      return JWT::encode($payload, $secretKey, 'HS256');
  }

  /**
   * Onboard a merchant
   */
  function onboardMerchant() {
      global $secretKey;

      $token = generateToken(1, 'merchant@gmail.com', $secretKey);

      $merchantData = [
          'businessType' => 'STARTER',
          'legalName' => 'Starbucks',
          'merchantName' => 'Starbucks',
          'modeOfPayments' => ['ECOM'],
          'currency' => 'PHP',
          'tin' => '455691852',
          'industry' => 'Food and Beverage',
          'serviceDescription' => 'Specialty coffee retailer',
          'isBrickAndMortarStore' => true,
          'address' => [
              'office' => [
                  'city' => 'Portland',
                  'line1' => '111 Clarence Basler Rd',
                  'buildingNameAndNumber' => 'Gresham Hub',
                  'state' => 'Oregon(OR)',
                  'postalCode' => '97030',
                  'contactNumber' => '639865748123',
                  'barangay' => 'Wilmington II'
              ]
          ],
          'websites' => [
              'businessWebsiteUrl' => 'https://www.starbucks.com/',
              'isBusinessWebsiteUnderDevelopment' => false
          ],
          'representatives' => [
              'authorized' => [
                  'firstName' => 'David',
                  'middleName' => 'Reese',
                  'lastName' => 'Haven',
                  'emailAddress' => 'david@haven.com',
                  'contactNumber' => '09789364458',
                  'dateOfBirth' => '1988-02-12T16:00:00.000Z',
                  'position' => 'Accountant'
              ]
          ],
          'banks' => [
              [
                  'accountDepositType' => 'GCASH',
                  'bankName' => 'GCASH',
                  'accountName' => 'Starbucks Corporation',
                  'accountNumber' => '09260000000'
              ],
              [
                  'accountDepositType' => 'BANK',
                  'accountType' => 'SAVINGS',
                  'bankName' => 'ASIA_UNITED_BANK',
                  'accountName' => 'Starbucks Corporation',
                  'accountNumber' => '4716542700323890',
                  'currency' => 'PHP'
              ]
          ]
      ];

      try {
          $client = new Client();
          $response = $client->post('https://kyc.sbx.moduluslabs.io/v2/onboard', [
              'headers' => [
                  'Authorization' => 'Bearer ' . $token,
                  'Content-Type' => 'application/json'
              ],
              'json' => $merchantData
          ]);

          $body = json_decode($response->getBody(), true);
          echo " Merchant onboarded successfully\n";
          echo "Reference Number: " . $body['referenceNumber'] . "\n";
          return $body;

      } catch (RequestException $e) {
          echo " Onboarding failed: " . $e->getMessage() . "\n";
          if ($e->hasResponse()) {
              echo $e->getResponse()->getBody() . "\n";
          }
          throw $e;
      }
  }

  onboardMerchant();
  ?>
  ```

  ```java Java theme={null}
  import com.google.gson.Gson;
  import io.jsonwebtoken.Jwts;
  import io.jsonwebtoken.SignatureAlgorithm;
  import okhttp3.*;

  import javax.crypto.spec.SecretKeySpec;
  import java.io.IOException;
  import java.security.Key;
  import java.util.*;

  public class OnboardMerchant {
      private static final String SECRET_KEY = System.getenv("MODULUS_ONBOARDING_SECRET_KEY");
      private static final String API_URL = "https://kyc.sbx.moduluslabs.io/v2/onboard";
      private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

      /**
       * Generate JWT token
       */
      public static String generateToken(int accountId, String email) {
          Key key = new SecretKeySpec(SECRET_KEY.getBytes(), SignatureAlgorithm.HS256.getJcaName());

          return Jwts.builder()
                  .claim("sub", accountId)
                  .claim("email", email)
                  .signWith(key, SignatureAlgorithm.HS256)
                  .compact();
      }

      /**
       * Onboard a merchant
       */
      public static void onboardMerchant() throws IOException {
          String token = generateToken(1, "merchant@gmail.com");

          // Create merchant data
          Map<String, Object> merchantData = new HashMap<>();
          merchantData.put("businessType", "STARTER");
          merchantData.put("legalName", "Starbucks");
          merchantData.put("merchantName", "Starbucks");
          merchantData.put("modeOfPayments", Arrays.asList("ECOM"));
          merchantData.put("currency", "PHP");
          merchantData.put("tin", "455691852");
          merchantData.put("industry", "Food and Beverage");
          merchantData.put("serviceDescription", "Specialty coffee retailer");
          merchantData.put("isBrickAndMortarStore", true);

          // Address
          Map<String, Object> office = new HashMap<>();
          office.put("city", "Portland");
          office.put("line1", "111 Clarence Basler Rd");
          office.put("buildingNameAndNumber", "Gresham Hub");
          office.put("state", "Oregon(OR)");
          office.put("postalCode", "97030");
          office.put("contactNumber", "639865748123");
          office.put("barangay", "Wilmington II");

          Map<String, Object> address = new HashMap<>();
          address.put("office", office);
          merchantData.put("address", address);

          // Websites
          Map<String, Object> websites = new HashMap<>();
          websites.put("businessWebsiteUrl", "https://www.starbucks.com/");
          websites.put("isBusinessWebsiteUnderDevelopment", false);
          merchantData.put("websites", websites);

          // Representatives
          Map<String, Object> authorized = new HashMap<>();
          authorized.put("firstName", "David");
          authorized.put("middleName", "Reese");
          authorized.put("lastName", "Haven");
          authorized.put("emailAddress", "david@haven.com");
          authorized.put("contactNumber", "09789364458");
          authorized.put("dateOfBirth", "1988-02-12T16:00:00.000Z");
          authorized.put("position", "Accountant");

          Map<String, Object> representatives = new HashMap<>();
          representatives.put("authorized", authorized);
          merchantData.put("representatives", representatives);

          // Banks
          Map<String, Object> bank1 = new HashMap<>();
          bank1.put("accountDepositType", "GCASH");
          bank1.put("bankName", "GCASH");
          bank1.put("accountName", "Starbucks Corporation");
          bank1.put("accountNumber", "09260000000");

          Map<String, Object> bank2 = new HashMap<>();
          bank2.put("accountDepositType", "BANK");
          bank2.put("accountType", "SAVINGS");
          bank2.put("bankName", "ASIA_UNITED_BANK");
          bank2.put("accountName", "Starbucks Corporation");
          bank2.put("accountNumber", "4716542700323890");
          bank2.put("currency", "PHP");

          merchantData.put("banks", Arrays.asList(bank1, bank2));

          // Send request
          Gson gson = new Gson();
          String json = gson.toJson(merchantData);

          OkHttpClient client = new OkHttpClient();
          RequestBody body = RequestBody.create(json, JSON);
          Request request = new Request.Builder()
                  .url(API_URL)
                  .header("Authorization", "Bearer " + token)
                  .post(body)
                  .build();

          try (Response response = client.newCall(request).execute()) {
              String responseBody = response.body().string();

              if (response.isSuccessful()) {
                  System.out.println(" Merchant onboarded successfully");
                  System.out.println("Response: " + responseBody);
              } else {
                  System.out.println(" Onboarding failed: HTTP " + response.code());
                  System.out.println("Details: " + responseBody);
              }
          }
      }

      public static void main(String[] args) {
          try {
              onboardMerchant();
          } catch (IOException e) {
              System.err.println(" Error: " + e.getMessage());
              e.printStackTrace();
          }
      }
  }
  ```

  ```go Go theme={null}
  package main

  import (
  	"bytes"
  	"encoding/json"
  	"fmt"
  	"io"
  	"net/http"
  	"os"
  	"time"

  	"github.com/golang-jwt/jwt/v5"
  )

  const apiURL = "https://kyc.sbx.moduluslabs.io/v2/onboard"

  // generateToken creates a JWT token
  func generateToken(accountID int, email, secretKey string) (string, error) {
  	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
  		"sub":   accountID,
  		"email": email,
  	})

  	return token.SignedString([]byte(secretKey))
  }

  // onboardMerchant sends the onboarding request
  func onboardMerchant() error {
  	secretKey := os.Getenv("MODULUS_ONBOARDING_SECRET_KEY")
  	if secretKey == "" {
  		return fmt.Errorf("MODULUS_ONBOARDING_SECRET_KEY environment variable not set")
  	}

  	token, err := generateToken(1, "merchant@gmail.com", secretKey)
  	if err != nil {
  		return fmt.Errorf("failed to generate token: %w", err)
  	}

  	merchantData := map[string]interface{}{
  		"businessType":          "STARTER",
  		"legalName":             "Starbucks",
  		"merchantName":          "Starbucks",
  		"modeOfPayments":        []string{"ECOM"},
  		"currency":              "PHP",
  		"tin":                   "455691852",
  		"industry":              "Food and Beverage",
  		"serviceDescription":    "Specialty coffee retailer",
  		"isBrickAndMortarStore": true,
  		"address": map[string]interface{}{
  			"office": map[string]interface{}{
  				"city":                  "Portland",
  				"line1":                 "111 Clarence Basler Rd",
  				"buildingNameAndNumber": "Gresham Hub",
  				"state":                 "Oregon(OR)",
  				"postalCode":            "97030",
  				"contactNumber":         "639865748123",
  				"barangay":              "Wilmington II",
  			},
  		},
  		"websites": map[string]interface{}{
  			"businessWebsiteUrl":                "https://www.starbucks.com/",
  			"isBusinessWebsiteUnderDevelopment": false,
  		},
  		"representatives": map[string]interface{}{
  			"authorized": map[string]interface{}{
  				"firstName":     "David",
  				"middleName":    "Reese",
  				"lastName":      "Haven",
  				"emailAddress":  "david@haven.com",
  				"contactNumber": "09789364458",
  				"dateOfBirth":   "1988-02-12T16:00:00.000Z",
  				"position":      "Accountant",
  			},
  		},
  		"banks": []map[string]interface{}{
  			{
  				"accountDepositType": "GCASH",
  				"bankName":           "GCASH",
  				"accountName":        "Starbucks Corporation",
  				"accountNumber":      "09260000000",
  			},
  			{
  				"accountDepositType": "BANK",
  				"accountType":        "SAVINGS",
  				"bankName":           "ASIA_UNITED_BANK",
  				"accountName":        "Starbucks Corporation",
  				"accountNumber":      "4716542700323890",
  				"currency":           "PHP",
  			},
  		},
  	}

  	jsonData, err := json.Marshal(merchantData)
  	if err != nil {
  		return fmt.Errorf("failed to marshal data: %w", err)
  	}

  	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
  	if err != nil {
  		return fmt.Errorf("failed to create request: %w", err)
  	}

  	req.Header.Set("Authorization", "Bearer "+token)
  	req.Header.Set("Content-Type", "application/json")

  	client := &http.Client{Timeout: 30 * time.Second}
  	resp, err := client.Do(req)
  	if err != nil {
  		return fmt.Errorf("request failed: %w", err)
  	}
  	defer resp.Body.Close()

  	body, err := io.ReadAll(resp.Body)
  	if err != nil {
  		return fmt.Errorf("failed to read response: %w", err)
  	}

  	if resp.StatusCode == http.StatusOK {
  		var result map[string]interface{}
  		if err := json.Unmarshal(body, &result); err != nil {
  			return fmt.Errorf("failed to parse response: %w", err)
  		}

  		fmt.Println(" Merchant onboarded successfully")
  		fmt.Printf("Reference Number: %s\n", result["referenceNumber"])
  		return nil
  	}

  	fmt.Printf(" Onboarding failed: HTTP %d\n", resp.StatusCode)
  	fmt.Printf("Details: %s\n", string(body))
  	return fmt.Errorf("onboarding failed with status %d", resp.StatusCode)
  }

  func main() {
  	if err := onboardMerchant(); err != nil {
  		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  		os.Exit(1)
  	}
  }
  ```

  ```csharp C# / .NET theme={null}
  using System;
  using System.Net.Http;
  using System.Net.Http.Headers;
  using System.Text;
  using System.Text.Json;
  using System.Threading.Tasks;
  using System.IdentityModel.Tokens.Jwt;  // Install-Package System.IdentityModel.Tokens.Jwt
  using Microsoft.IdentityModel.Tokens;

  class Program
  {
      static async Task Main(string[] args)
      {
          await OnboardMerchant();
      }

      static string GenerateToken(int accountId, string email, string secretKey)
      {
          var tokenHandler = new JwtSecurityTokenHandler();
          var key = Encoding.UTF8.GetBytes(secretKey);

          var tokenDescriptor = new SecurityTokenDescriptor
          {
              Subject = new System.Security.Claims.ClaimsIdentity(new[]
              {
                  new System.Security.Claims.Claim("sub", accountId.ToString()),
                  new System.Security.Claims.Claim("email", email)
              }),
              SigningCredentials = new SigningCredentials(
                  new SymmetricSecurityKey(key),
                  SecurityAlgorithms.HmacSha256Signature
              )
          };

          var token = tokenHandler.CreateToken(tokenDescriptor);
          return tokenHandler.WriteToken(token);
      }

      static async Task OnboardMerchant()
      {
          // Get secret key from environment variable
          var secretKey = Environment.GetEnvironmentVariable("MODULUS_ONBOARDING_SECRET_KEY");

          if (string.IsNullOrEmpty(secretKey))
          {
              Console.WriteLine(" Error: MODULUS_ONBOARDING_SECRET_KEY environment variable not set");
              return;
          }

          // Generate JWT token
          var token = GenerateToken(1, "merchant@gmail.com", secretKey);

          try
          {
              // Create HTTP client with Bearer token authentication
              using var client = new HttpClient();
              client.DefaultRequestHeaders.Authorization =
                  new AuthenticationHeaderValue("Bearer", token);

              // Prepare merchant data
              var merchantData = new
              {
                  businessType = "STARTER",
                  legalName = "Starbucks",
                  merchantName = "Starbucks",
                  modeOfPayments = new[] { "ECOM" },
                  currency = "PHP",
                  tin = "455691852",
                  industry = "Food and Beverage",
                  serviceDescription = "Specialty coffee retailer",
                  isBrickAndMortarStore = true,
                  address = new
                  {
                      office = new
                      {
                          city = "Portland",
                          line1 = "111 Clarence Basler Rd",
                          buildingNameAndNumber = "Gresham Hub",
                          state = "Oregon(OR)",
                          postalCode = "97030",
                          contactNumber = "639865748123",
                          barangay = "Wilmington II"
                      }
                  },
                  websites = new
                  {
                      businessWebsiteUrl = "https://www.starbucks.com/",
                      isBusinessWebsiteUnderDevelopment = false
                  },
                  representatives = new
                  {
                      authorized = new
                      {
                          firstName = "David",
                          middleName = "Reese",
                          lastName = "Haven",
                          emailAddress = "david@haven.com",
                          contactNumber = "09789364458",
                          dateOfBirth = "1988-02-12T16:00:00.000Z",
                          position = "Accountant"
                      }
                  },
                  banks = new[]
                  {
                      new
                      {
                          accountDepositType = "GCASH",
                          bankName = "GCASH",
                          accountName = "Starbucks Corporation",
                          accountNumber = "09260000000"
                      },
                      new
                      {
                          accountDepositType = "BANK",
                          accountType = "SAVINGS",
                          bankName = "ASIA_UNITED_BANK",
                          accountName = "Starbucks Corporation",
                          accountNumber = "4716542700323890",
                          currency = "PHP"
                      }
                  }
              };

              var content = new StringContent(
                  JsonSerializer.Serialize(merchantData),
                  Encoding.UTF8,
                  "application/json"
              );

              // Send POST request
              var response = await client.PostAsync(
                  "https://kyc.sbx.moduluslabs.io/v2/onboard",
                  content
              );

              var responseBody = await response.Content.ReadAsStringAsync();

              if (response.IsSuccessStatusCode)
              {
                  var result = JsonSerializer.Deserialize<OnboardResponse>(
                      responseBody,
                      new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
                  );

                  Console.WriteLine(" Merchant onboarded successfully");
                  Console.WriteLine($"Reference Number: {result.ReferenceNumber}");
              }
              else
              {
                  Console.WriteLine($" Onboarding failed: HTTP {(int)response.StatusCode}");
                  Console.WriteLine($"Details: {responseBody}");
              }
          }
          catch (HttpRequestException e)
          {
              Console.WriteLine($" Request failed: {e.Message}");
          }
          catch (Exception e)
          {
              Console.WriteLine($" Error: {e.Message}");
          }
      }
  }

  // Response model
  public class OnboardResponse
  {
      public string ReferenceNumber { get; set; }
  }
  ```
</RequestExample>

## Business Type Examples

<Tabs>
  <Tab title="Starter Business">
    ```json theme={null}
    {
      "businessType": "STARTER",
      "merchantName": "My Coffee Shop",
      "modeOfPayments": ["ECOM", "QRPH"],
      "currency": "PHP",
      "tin": "123456789",
      "industry": "Food and Beverage",
      "serviceDescription": "Local coffee shop",
      "isBrickAndMortarStore": true,
      "address": {
        "office": { /* required */ }
      },
      "representatives": {
        "authorized": { /* required */ }
      },
      "banks": [ /* at least one required */ ]
    }
    ```

    <Note>
      **Starter businesses** don't require `legalName`, `incorporators`, or `registered` address.
    </Note>
  </Tab>

  <Tab title="Sole Proprietor">
    ```json theme={null}
    {
      "businessType": "SOLE_PROPRIETOR",
      "legalName": "Juan Dela Cruz Trading",
      "merchantName": "JDC Store",
      "incorporators": [
        {
          "firstName": "Juan",
          "lastName": "Dela Cruz",
          "emailAddress": "juan@email.com",
          "contactNumber": "09123456789",
          "natureOfWork": "Retail",
          "sourceOfFunds": "BUSINESS_INCOME",
          "nationality": "Filipino",
          "dateOfBirth": "1985-01-15T00:00:00.000Z",
          "address": { /* required */ }
        }
      ],
      "address": {
        "office": { /* required */ },
        "registered": { /* required */ }
      },
      /* ... other required fields ... */
    }
    ```

    <Tip>
      **Exactly 1 incorporator** required for Sole Proprietor businesses.
    </Tip>
  </Tab>

  <Tab title="Partnership">
    ```json theme={null}
    {
      "businessType": "PARTNERSHIP",
      "legalName": "Smith & Jones Partnership",
      "merchantName": "S&J Enterprises",
      "incorporators": [
        {
          "firstName": "John",
          "lastName": "Smith",
          /* ... complete details ... */
        },
        {
          "firstName": "Mary",
          "lastName": "Jones",
          /* ... complete details ... */
        }
      ],
      "address": {
        "office": { /* required */ },
        "registered": { /* required */ }
      },
      /* ... other required fields ... */
    }
    ```

    <Tip>
      **Exactly 2 incorporators** required for Partnership businesses.
    </Tip>
  </Tab>

  <Tab title="Corporation">
    ```json theme={null}
    {
      "businessType": "CORPORATION",
      "legalName": "Acme Corporation Inc.",
      "merchantName": "Acme Corp",
      "incorporators": [
        { /* incorporator 1 - complete details */ },
        { /* incorporator 2 - complete details */ },
        { /* incorporator 3 - complete details */ }
      ],
      "signatories": [
        {
          "firstName": "Jane",
          "lastName": "Doe",
          "position": "CEO"
        }
      ],
      "address": {
        "office": { /* required */ },
        "registered": { /* required */ }
      },
      /* ... other required fields ... */
    }
    ```

    <Tip>
      **Minimum 3 incorporators** required for Corporation businesses.
    </Tip>
  </Tab>
</Tabs>

## Best Practices

<CardGroup cols={2}>
  <Card title="Upload Documents First" icon="upload">
    Always upload required KYC documents before calling this API
  </Card>

  <Card title="Validate Before Submit" icon="shield-check">
    Validate all parameters client-side to catch errors early
  </Card>

  <Card title="Use Correct Business Type" icon="building">
    Choose the appropriate business type and provide required fields
  </Card>

  <Card title="Test with Sandbox" icon="flask">
    Test thoroughly in sandbox before going to production
  </Card>

  <Card title="Store Reference Number" icon="database">
    Save the returned reference number for status tracking
  </Card>

  <Card title="Handle Errors Gracefully" icon="triangle-exclamation">
    Implement proper error handling and user feedback
  </Card>
</CardGroup>

## Validation Checklist

Before submitting:

<Steps>
  <Step title="Business Type">
    ✓ Business type matches merchant entity type
    ✓ All required fields for business type are included
  </Step>

  <Step title="Incorporators">
    ✓ Correct number of incorporators for business type
    ✓ All incorporator fields complete and valid
  </Step>

  <Step title="Addresses">
    ✓ Office address complete
    ✓ Registered address included if not STARTER
    ✓ All numeric fields contain only digits
  </Step>

  <Step title="Bank Accounts">
    ✓ At least one bank account provided
    ✓ Bank name matches account deposit type
    ✓ All required fields for deposit type included
  </Step>

  <Step title="Documents">
    ✓ All required documents uploaded via File Upload API
    ✓ Document types match business type requirements
  </Step>
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Store Reference Number" icon="database" href="/docs/onboarding/introduction#reference-tracking">
    Save the reference number for tracking and support requests
  </Card>

  <Card title="Enums Reference" icon="list" href="/docs/onboarding/enums">
    All valid enum values for parameters
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/docs/errors">
    Handle onboarding errors properly
  </Card>

  <Card title="Authentication" icon="key" href="/docs/onboarding/authentication">
    JWT Bearer Token authentication guide
  </Card>
</CardGroup>


## OpenAPI

````yaml POST /v2/onboard
openapi: 3.1.0
info:
  title: Modulus Labs Onboarding API
  description: API for merchant onboarding, KYC verification, and account management
  version: 2.0.0
servers:
  - url: https://kyc.sbx.moduluslabs.io
    description: Sandbox
security: []
paths:
  /v2/onboard:
    post:
      tags:
        - Onboarding
      summary: Onboard Merchant
      description: >-
        Submit merchant onboarding application with business details,
        representatives, and bank account information.
      operationId: onboardMerchant
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OnboardMerchantRequest'
      responses:
        '201':
          description: Onboarding application submitted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OnboardMerchantResponse'
        '400':
          description: Bad Request - Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized - Invalid or expired token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - bearerAuth: []
components:
  schemas:
    OnboardMerchantRequest:
      type: object
      required:
        - merchantName
        - modeOfPayments
        - currency
        - tin
        - industry
        - serviceDescription
        - isBrickAndMortarStore
        - address
        - representatives
        - banks
      properties:
        legalName:
          type: string
          maxLength: 500
          description: >-
            Legal registered name of the business (required for non-STARTER
            types)
        merchantName:
          type: string
          maxLength: 500
          description: Trading name or DBA (Doing Business As) name
        businessHandle:
          type: string
          pattern: ^([A-Za-z]|[0-9]|_|-)+$
          description: >-
            Unique business handle/slug for payment links (required for
            non-STARTER types)
        modeOfPayments:
          type: array
          minItems: 1
          items:
            type: string
            enum:
              - CREDIT_CARD
              - DEBIT_CARD
              - GCASH
              - GRABPAY
              - PAYMAYA
              - BANK_TRANSFER
          description: List of accepted payment modes
        currency:
          type: string
          enum:
            - PHP
            - USD
          description: Primary currency for transactions
        tin:
          type: string
          pattern: ^\d+$
          maxLength: 20
          description: Tax Identification Number
        industry:
          type: string
          maxLength: 200
          description: Business industry category
        serviceDescription:
          type: string
          description: Description of services or products offered
        isBrickAndMortarStore:
          type: boolean
          description: Whether the business has a physical store location
        address:
          $ref: '#/components/schemas/BusinessAddress'
        representatives:
          $ref: '#/components/schemas/Representatives'
        banks:
          type: array
          minItems: 1
          maxItems: 2
          items:
            $ref: '#/components/schemas/BankAccount'
          description: List of bank accounts for settlement
        incorporators:
          type: array
          minItems: 1
          maxItems: 5
          items:
            $ref: '#/components/schemas/Incorporator'
          description: List of business incorporators (for non-STARTER types)
        signatories:
          type: array
          minItems: 1
          maxItems: 5
          items:
            $ref: '#/components/schemas/Signatory'
          description: List of authorized signatories
        websites:
          $ref: '#/components/schemas/Websites'
    OnboardMerchantResponse:
      type: object
      properties:
        onboardingStatus:
          type: string
          enum:
            - NEW
          description: Initial status of the onboarding application
        onboardingReferenceNumber:
          type: string
          format: uuid
          description: Unique reference number for the onboarding record
    ErrorResponse:
      type: object
      properties:
        statusCode:
          type: integer
          description: HTTP status code
        message:
          type: string
          description: Error message
        error:
          type: string
          description: Error type
        details:
          type: object
          description: Additional error details
    BusinessAddress:
      type: object
      required:
        - office
      properties:
        office:
          $ref: '#/components/schemas/Address'
        registered:
          $ref: '#/components/schemas/Address'
    Representatives:
      type: object
      required:
        - authorized
      properties:
        authorized:
          $ref: '#/components/schemas/AuthorizedRepresentative'
        escalation:
          $ref: '#/components/schemas/EscalationRepresentative'
    BankAccount:
      type: object
      required:
        - accountDepositType
        - currency
      properties:
        accountDepositType:
          type: string
          enum:
            - BANK
            - PAYMAYA
            - GCASH
            - NO_ACCOUNT
          description: Type of deposit account
        accountType:
          type: string
          enum:
            - SAVINGS
            - CHECKING_CURRENT
          description: Type of bank account (required when accountDepositType is BANK)
        bankName:
          type: string
          maxLength: 500
          description: >-
            Name of the bank (required when accountDepositType is not
            NO_ACCOUNT)
        accountName:
          type: string
          maxLength: 500
          description: Name on the bank account
        accountNumber:
          type: string
          maxLength: 500
          description: Bank account number (will be encrypted)
        currency:
          type: string
          enum:
            - PHP
            - USD
          description: Currency of the bank account
        preferredSettlementRail:
          type: string
          enum:
            - instapay
            - pesonet
          description: Preferred settlement rail for fund transfers
        bankCode:
          type: string
          pattern: ^[A-Z0-9]{8,11}$
          description: SWIFT/BIC code
    Incorporator:
      type: object
      required:
        - firstName
        - lastName
        - emailAddress
        - contactNumber
        - dateOfBirth
        - address
      properties:
        firstName:
          type: string
          maxLength: 100
        middleName:
          type: string
          maxLength: 100
        lastName:
          type: string
          maxLength: 100
        emailAddress:
          type: string
          format: email
          maxLength: 100
        contactNumber:
          type: string
          pattern: ^\d+$
          maxLength: 30
        dateOfBirth:
          type: string
          format: date
        address:
          $ref: '#/components/schemas/Address'
    Signatory:
      type: object
      required:
        - firstName
        - lastName
        - position
      properties:
        firstName:
          type: string
          maxLength: 100
        middleName:
          type: string
          maxLength: 100
        lastName:
          type: string
          maxLength: 100
        position:
          type: string
          maxLength: 100
          description: Position/title
    Websites:
      type: object
      properties:
        businessWebsiteUrl:
          type: string
          format: uri
          description: Main business website URL
        isBusinessWebsiteUnderDevelopment:
          type: boolean
          description: >-
            Whether the website is still under development (required if
            businessWebsiteUrl provided)
        facebookUrl:
          type: string
          format: uri
        twitterUrl:
          type: string
          format: uri
        instagramUrl:
          type: string
          format: uri
        tiktokUrl:
          type: string
          format: uri
    Address:
      type: object
      required:
        - city
        - line1
        - state
        - postalCode
        - contactNumber
        - barangay
      properties:
        city:
          type: string
          maxLength: 100
          description: City
        line1:
          type: string
          maxLength: 64
          description: Street address
        buildingNameAndNumber:
          type: string
          maxLength: 200
          description: Building name and number
        state:
          type: string
          maxLength: 64
          description: Province/State
        postalCode:
          type: string
          pattern: ^\d{4,10}$
          description: Postal code (4-10 digits)
        contactNumber:
          type: string
          pattern: ^\d+$
          maxLength: 30
          description: Contact number (digits only)
        barangay:
          type: string
          maxLength: 200
          description: Barangay
    AuthorizedRepresentative:
      type: object
      required:
        - firstName
        - lastName
        - emailAddress
        - contactNumber
        - dateOfBirth
        - position
      properties:
        firstName:
          type: string
          maxLength: 100
        middleName:
          type: string
          maxLength: 100
        lastName:
          type: string
          maxLength: 100
        emailAddress:
          type: string
          format: email
          maxLength: 100
        contactNumber:
          type: string
          pattern: ^\d+$
          maxLength: 30
        dateOfBirth:
          type: string
          format: date
          description: Date of birth (ISO 8601 date format)
        position:
          type: string
          maxLength: 100
          description: Job position/title
        tin:
          type: string
          pattern: ^\d+$
          maxLength: 20
        sss:
          type: string
          pattern: ^\d{10}$
          description: SSS number (exactly 10 digits)
        mothersMaidenName:
          type: string
          maxLength: 255
        gender:
          type: string
          enum:
            - MALE
            - FEMALE
    EscalationRepresentative:
      type: object
      properties:
        firstName:
          type: string
          maxLength: 100
        lastName:
          type: string
          maxLength: 100
        emailAddress:
          type: string
          format: email
        contactNumber:
          type: string
          pattern: ^\d+$
        dateOfBirth:
          type: string
          format: date
        tin:
          type: string
          pattern: ^\d{9}$
        sss:
          type: string
          pattern: ^\d{10}$
        position:
          type: string
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT Bearer token authentication

````