Onboard business via API

This guide explains how to onboard a business customer (KYB) through a direct API integration. You collect company details, associated persons (directors, UBOs, representatives), and corporate documents directly inside your application, and submit them to Harbor in a single API request.


1. Prerequisites & Endpoints

Before submitting business onboarding, ensure that:

  1. The customer has been created as a business customer (POST /api/v1/customers).
  2. The customer has signed the required platform agreement.
  3. The customer is not already KYC/KYB verified.

Endpoints

MethodPathPurpose
POST/api/v1/customers/{{customer_uuid}}/onboardingSubmit the full business onboarding payload. Returns 202.
GET/api/v1/customers/{{customer_uuid}}/onboardingRead the latest onboarding status.
PATCH/api/v1/customers/{{customer_uuid}}/onboardingCorrect/replace data while status is action_required. Returns 202.

2. Business Meta APIs (Reference-Data Lookups)

When collecting business onboarding data, some fields only accept specific values from dynamic lists that change over time. You must call these lookup endpoints to get the valid options for your UI:

FieldEndpointScopePurpose
company.industryGET /api/v1/countries/{country}/industriesPer-countryFetch valid industry slugs for the country of incorporation.
associated_persons[].positionGET /api/v1/job-titlesGlobalFetch valid job titles/positions for associated persons.
Address stateGET /api/v1/countries/{country}/subdivisionsPer-countryFetch valid state subdivision codes (e.g., VA, WA).

3. Submitting Business Onboarding (POST)

Submit the company profile, all required associated persons, and corporate files (base64-encoded) in a single request.

Request

curl -X POST "https://harbor-sandbox.owlpay.com/api/v1/customers/{{customer_uuid}}/onboarding" \
  -H "X-API-KEY: ***" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: {{Idempotency-Key}}" \
  -d @business-onboarding.json

Full Payload Example

{
  "company": {
    "name": "Acme US Inc",
    "business_name": "Acme",
    "registration_number": "REG123456",
    "country": "US",
    "incorporation_date": "2020-01-01",
    "type": "corporation",
    "description": "Software publishers",
    "industry": "software_publishers",
    "register_address": {
      "street": "100 Market St",
      "city": "Richmond",
      "state": "VA",
      "postal_code": "23218"
    },
    "business_address": {
      "street": "100 Market St",
      "city": "Richmond",
      "state": "VA",
      "country": "US",
      "postal_code": "23218"
    },
    "phone": {
      "country": "US",
      "number": "2025550100"
    },
    "business_phone": null,
    "email": "[email protected]",
    "url": "https://acme.us",
    "has_bearer_shares": false,
    "purpose_of_registration": "receiptAndPayment",
    "estimated_number_of_transactions": 100,
    "estimated_amount_of_transactions": 50000,
    "country_of_operation": [
      "US",
      "CA"
    ],
    "has_vasp_relationship": false,
    "source_of_funds": "BUSINESS_INCOME",
    "source_of_funds_other": null,
    "transfers_funds_for_clients": true,
    "compliance_screening_explanation": "Funds are settlement proceeds from merchant sales.",
    "tax_id": "123456789"
  },
  "associated_persons": [
    {
      "type": "representative",
      "first_name": "Ada",
      "last_name": "Smith",
      "id_type": "PASSPORT",
      "id_number": "A1234567",
      "birthday": "1990-01-01",
      "gender": "F",
      "nationality": "US",
      "position": "director",
      "residential_address": {
        "street": "200 Pine St",
        "city": "Richmond",
        "state": "VA",
        "country": "US",
        "postal_code": "23219"
      },
      "permanent_address": {
        "street": "200 Pine St",
        "city": "Richmond",
        "state": "VA",
        "country": "US",
        "postal_code": "23219"
      },
      "phone": {
        "country": "US",
        "number": "4155550101"
      },
      "email": "[email protected]",
      "identity_files": [
        {
          "file_type": "PASSPORT",
          "part": "FRONT",
          "country": "US",
          "file": "<base64 bytes>"
        }
      ]
    },
    {
      "type": "applicant",
      "first_name": "Ben",
      "last_name": "Jones",
      "id_type": "PASSPORT",
      "id_number": "B7654321",
      "birthday": "1988-03-03",
      "gender": "M",
      "nationality": "US",
      "email": "[email protected]",
      "residential_address": {
        "street": "300 Bush St",
        "city": "Richmond",
        "state": "VA",
        "country": "US",
        "postal_code": "23219"
      },
      "identity_files": [
        {
          "file_type": "PASSPORT",
          "part": "FRONT",
          "country": "US",
          "file": "<base64 bytes>"
        }
      ]
    }
  ],
  "company_files": [
    {
      "file_type": "corporate_registration_documents",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "documents_of_corporate_governance_and_amendment",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "certificate_of_incumbency",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "proof_of_residential_address_of_the_ubo",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "proof_of_business_operating_address",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "organization_chart",
      "files": [
        "<base64 bytes>"
      ]
    }
  ]
}

Minimal Required-Fields Payload

{
  "company": {
    "name": "Acme US Inc",
    "business_name": "Acme",
    "registration_number": "REG123456",
    "country": "US",
    "incorporation_date": "2020-01-01",
    "type": "corporation",
    "description": "Software publishers",
    "industry": "software_publishers",
    "register_address": {
      "street": "100 Main St",
      "city": "Richmond",
      "state": "VA",
      "postal_code": "23218"
    },
    "business_address": {
      "street": "100 Main St",
      "city": "Richmond",
      "state": "VA",
      "country": "US",
      "postal_code": "23218"
    },
    "phone": {
      "country": "US",
      "number": "2025550100"
    },
    "email": "[email protected]",
    "url": "https://acme.us",
    "has_bearer_shares": false,
    "purpose_of_registration": "receiptAndPayment",
    "estimated_number_of_transactions": 100,
    "estimated_amount_of_transactions": 50000,
    "country_of_operation": [
      "US"
    ],
    "has_vasp_relationship": false,
    "source_of_funds": "BUSINESS_INCOME",
    "transfers_funds_for_clients": false,
    "tax_id": "123456789"
  },
  "associated_persons": [
    {
      "type": "representative",
      "first_name": "Ada",
      "last_name": "Smith",
      "id_type": "PASSPORT",
      "id_number": "A1234567",
      "birthday": "1990-01-01",
      "gender": "F",
      "nationality": "US",
      "position": "director",
      "residential_address": {
        "street": "200 Pine St",
        "city": "Richmond",
        "state": "VA",
        "country": "US",
        "postal_code": "23219"
      },
      "permanent_address": {
        "street": "200 Pine St",
        "city": "Richmond",
        "state": "VA",
        "country": "US",
        "postal_code": "23219"
      },
      "phone": {
        "country": "US",
        "number": "4045550101"
      },
      "identity_files": [
        {
          "file_type": "PASSPORT",
          "part": "FRONT",
          "country": "US",
          "file": "<base64 bytes>"
        }
      ]
    },
    {
      "type": "applicant",
      "first_name": "Ben",
      "last_name": "Jones",
      "id_type": "PASSPORT",
      "id_number": "B7654321",
      "birthday": "1988-03-03",
      "gender": "M",
      "nationality": "US",
      "email": "[email protected]",
      "residential_address": {
        "street": "300 Bush St",
        "city": "Richmond",
        "state": "VA",
        "country": "US",
        "postal_code": "23219"
      },
      "identity_files": [
        {
          "file_type": "PASSPORT",
          "part": "FRONT",
          "country": "US",
          "file": "<base64 bytes>"
        }
      ]
    }
  ],
  "company_files": [
    {
      "file_type": "corporate_registration_documents",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "documents_of_corporate_governance_and_amendment",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "certificate_of_incumbency",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "proof_of_residential_address_of_the_ubo",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "proof_of_business_operating_address",
      "files": [
        "<base64 bytes>"
      ]
    },
    {
      "file_type": "organization_chart",
      "files": [
        "<base64 bytes>"
      ]
    }
  ]
}

Response (202 Accepted)

{
  "data": {
    "id": "onb_a1b2c3…",
    "customer_id": "cus_1234567890",
    "status": "processing",
    "submitted_at": null,
    "created_at": "2026-06-13T09:00:00.000000Z",
    "updated_at": "2026-06-13T09:00:00.000000Z"
  }
}

4. Field Reference

4.1 company (all required unless noted)

FieldType / formatNotes
namestring ≤255Registered legal name.
business_namestring ≤255Trading / DBA name.
registration_numberstring ≤255
countryISO 3166-1 alpha-2Country of incorporation.
incorporation_dateYYYY-MM-DD
typeenumSee Company types below.
descriptionstring ≤1000Business description.
industrystringIndustry classification. A country-specific slug — see Industry classification lookup.
register_addressobject{ street, city, state?, postal_code }. state subdivision code from the lookup (e.g. VA).
business_addressobject{ street, city, state?, country, postal_code }. state is a subdivision code.
phoneobject{ country, number }country ISO 3166-1 alpha-2, number digits only.
business_phoneobject | null{ country, number }. If omitted/null, falls back to phone.
emailemail ≤255
urlURL ≤255
has_bearer_sharesbooleanWhether the company has bearer shares.
purpose_of_registrationenumonlyReceiving | receiptAndPayment.
estimated_number_of_transactionsinteger ≥0
estimated_amount_of_transactionsinteger ≥0
country_of_operationarray of ISO-2At least one.
source_of_fundsenumSee Source of funds below.
source_of_funds_otherstring ≤255 | nullRequired when source_of_funds is OTHER.
transfers_funds_for_clientsbooleanDo you transfer funds on behalf of your clients?
compliance_screening_explanationstring ≤1000 | nullOptional.
tax_idstring ≤255Company tax / registration id (VAT / EIN, etc.).
  • Company types: soleProprietorship, partnerships, limitedLiabilityCompany, corporation, corporationPublic, cooperative, nonProfitOrganization, stateOwnedCompany, trust, other.
  • Source of funds: BUSINESS_INCOME, CAPITAL_INJECTION, BANK_LOAN_OR_OTHER_BORROWINGS, INVESTMENT_INCOME, GOVERNMENT_GRANT, OTHER.

4.2 associated_persons[]

Provide associated persons by role:

typeRoleRequirement
representativeLegal representativeRequired — exactly one.
applicantApplicantRequired — at least one.
equity_holderUltimate beneficial owner (UBO)Optional. Anyone owning >20% is a UBO; if no one owns >20%, provide the individual who actually controls the company.
controllerSenior managing officialOptional. Required when the company is publicly listed, a non-profit, a government body, or state-owned, or when no shareholder owns >20%.

Common fields (all person types):

FieldType / formatNotes
typeenumrepresentative | applicant | equity_holder | controller.
first_name, last_namestring ≤255
id_typeenumNATIONAL_ID | PASSPORT | DRIVER_LICENCE | RESIDENCE_PERMIT.
id_numberstring ≤255
birthdayYYYY-MM-DD
genderM | F
nationalityISO-2
residential_addressobject{ street, city, state?, country, postal_code }. state is a subdivision code.
identity_filesarrayAt least one (see Files below).

Conditionally required, by type:

TypeAdditionally required
representativeposition, phone, permanent_address
applicantemail
controllerposition
equity_holderequity, has_control, is_signer, relationship_established_at, tax_jurisdiction_country, email, phone, permanent_address

Optional / conditional field shapes:

FieldType / format
positionA job-title value from the job-titles lookup.
emailemail ≤255
phone{ country, number } (country ISO-2, number digits only)
permanent_address{ street, city, state?, country, postal_code }
equitynumber 0–100 (percent)
has_controlboolean
is_signerboolean
relationship_established_atYYYY-MM-DD
tax_jurisdiction_countryISO-2

4.3 Files

Identity files (associated_persons[].identity_files[])

FieldTypeNotes
file_typeenumNATIONAL_ID | PASSPORT | DRIVER_LICENCE | RESIDENCE_PERMIT.
partFRONT | BACK
countryISO-2Issuing country.
filebase64 stringThe raw document bytes, base64-encoded.

Company files (company_files[])

FieldTypeNotes
file_typeenumOne entry per file_type (must be distinct). See list below.
filesarray of base64 stringsOne or more documents for that type.

Company file types (The first 6 are required on every submission):

file_typeRequiredMeaning
corporate_registration_documentsYesCompany registration documents
documents_of_corporate_governance_and_amendmentYesCorporate governance & amendment documents
certificate_of_incumbencyYesCertificate of incumbency
proof_of_residential_address_of_the_uboYesProof of residential address of the UBO
proof_of_business_operating_addressYesProof of business operating address
organization_chartYesOrganization chart
initial_capitalNoOptional
income_sourceNoOptional
business_licenseNoOptional
executive_documentsNoOptional
financial_documentsNoOptional
compliance_documentsNoOptional
other_documentationNoOptional

Size limits

  • Per file: ≤ 10 MB (decoded).
  • Whole request: ≤ 50 MB (decoded total across all files).

5. Fixing action_required

When status is action_required, the onboarding carries pending_requirements:

{
  "data": {
    "id": "onb_a1b2c3…",
    "status": "action_required",
    "pending_requirements": [
      {
        "source": "provider_error",
        "description": "companyUrl is invalid"
      }
    ]
  }
}

Correct the data with PATCH. You may replace any section — company, associated_persons, and/or company_files — not just the flagged fields. Each section you include replaces that section wholesale; sections you omit are left untouched.

curl -X PATCH "https://harbor-sandbox.owlpay.com/api/v1/customers/{{customer_uuid}}/onboarding" \
  -H "X-API-KEY: {{API_KEY}}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: {{Idempotency-Key}}" \
  -d '{
    "company": {
      "url": "https://www.acme.tw"
      /* …include ALL other company fields too — the section is replaced wholesale… */
    }
  }'

The response is 202; the onboarding goes back to processing and is automatically re-checked.

📘

Rules for PATCH

  • Allowed only while status is action_required.
  • A provided section must be complete and valid on its own, because it replaces the stored section entirely.
  • To change an associated person, include the full associated_persons array in the order you want; persons you drop are removed.