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

> ## Agent Instructions
> Document only operations in the linked OpenAPI contract: the source-data ingestion operations and the source-scoped outbound webhook management operations.
> Treat the generated OpenAPI document as authoritative for request fields. Never invent compatibility fields, alternate endpoints, or additional source models.
> Preserve explicit nulls, decimal strings, timestamps, source identifiers, immutable version identifiers, and document-version links exactly as defined.

# Onboard a business and its UBOs

> Create one business Customer, two individual UBO Customers, their relationships, and a KYB submission in one atomic request.

Karsa represents both people and businesses as Customers. Set `type` to `BUSINESS` when creating a
business and `INDIVIDUAL` when creating a person.

There are two supported ways to provide business data:

| What you need | Route | Result |
| - | - | - |
| Create or version one business only | `POST /api/ingestion/customers` | One `BUSINESS` Customer |
| Guarantee the business and every UBO are accepted together | `POST /api/ingestion/business-onboardings` | One `BUSINESS` Customer, one `INDIVIDUAL` Customer per UBO, relationships, and a KYB submission |

The atomic route is a convenience operation over the Customer model. It does not create a separate
kind of Business resource.

## Before you send the request

You need:

* your Karsa API base URL;
* a source-bound bearer credential;
* one stable source identifier for the business;
* one stable source identifier for every UBO;
* unique source identifiers for every ownership and role relationship; and
* one stable `Idempotency-Key` for this complete onboarding attempt.

Use identifiers from your own system. Do not generate a new `externalId` when retrying the same
customer.

Treat `dossier.sourceEffectiveAt` as the business-time cutoff for the complete snapshot. The
business and every beneficial owner's `effectiveAt` must be at or before that cutoff. Karsa may
receive and record the request later; those availability timestamps do not move the historical
business-time cutoff.

One request supports at most 50 beneficial owners, 10 role assignments per owner, and 100 total
ownership plus role relationships. The complete dossier rules also apply: relationship IDs and
provider evidence type/source-record pairs must be unique, every referenced related party must be
declared, and all provider evidence must belong to one package with one snapshot observation time
no later than `sourceEffectiveAt`.

## Send the canonical three-Customer request

This request creates Example Manufacturing LLC plus Ada and Grace as two separate individual
Customers. The two ownership percentages add to 100%, but Karsa does not require every business to
use that exact total because sources may provide incomplete or indirect ownership.

```bash theme={null}
curl --request POST \
  --url "$KARSA_API_URL/api/ingestion/business-onboardings" \
  --header "Authorization: Bearer $KARSA_API_TOKEN" \
  --header "Idempotency-Key: business-example-001-onboarding-v1" \
  --header "Content-Type: application/json" \
  --data '{
    "business": {
      "externalId": "customer-example-001",
      "type": "BUSINESS",
      "effectiveAt": "2026-08-05T14:00:00Z",
      "lifecycleStatus": "ACTIVE",
      "onboardedAt": "2026-08-05T13:45:00Z",
      "business": {
        "verificationTier": {
          "sourceText": "standard",
          "category": "STANDARD"
        },
        "onboardingContext": {
          "ipAddress": "203.0.113.10",
          "latitude": 39.1582,
          "longitude": -75.5244
        },
        "legalName": "Example Manufacturing LLC",
        "tradeNames": null,
        "legalForm": null,
        "formationJurisdiction": null,
        "formationDate": null,
        "registrationNumber": null,
        "taxIdentifiers": null,
        "registeredAddress": null,
        "email": "compliance@example.invalid",
        "website": null,
        "operatingAddress": null,
        "businessDescription": null,
        "industry": null,
        "accountPurpose": null,
        "transactionPurpose": "Pay suppliers and receive customer payments",
        "sourceOfFunds": null,
        "sourceOfWealth": "Operating revenue and retained earnings",
        "annualRevenue": null,
        "expectedMonthlyAmount": null,
        "expectedMonthlyTransactionCount": null,
        "operatingCountries": null,
        "handlesThirdPartyFunds": null,
        "publiclyTraded": false,
        "regulatedActivities": null
      },
      "documents": []
    },
    "beneficialOwners": [
      {
        "customer": {
          "externalId": "owner-example-001",
          "type": "INDIVIDUAL",
          "effectiveAt": "2026-08-21T12:00:00Z",
          "lifecycleStatus": "ACTIVE",
          "documents": [],
          "individual": {
            "legalName": "Ada Example",
            "givenName": "Ada",
            "middleName": null,
            "familyName": "Example",
            "dateOfBirth": "1985-06-15",
            "nationalities": ["US"],
            "residenceCountry": "US",
            "residentialAddress": null,
            "email": "ada@example.com",
            "phone": null,
            "taxIdentifiers": null
          }
        },
        "ownership": {
          "relationshipExternalId": "ownership-example-001",
          "ownershipPercent": 60
        },
        "roles": [
          {
            "relationshipExternalId": "control-example-001",
            "role": "CONTROL_PERSON"
          }
        ]
      },
      {
        "customer": {
          "externalId": "owner-example-002",
          "type": "INDIVIDUAL",
          "effectiveAt": "2026-08-21T12:00:00Z",
          "lifecycleStatus": "ACTIVE",
          "documents": [],
          "individual": {
            "legalName": "Grace Example",
            "givenName": "Grace",
            "middleName": null,
            "familyName": "Example",
            "dateOfBirth": "1988-12-09",
            "nationalities": ["US"],
            "residenceCountry": "US",
            "residentialAddress": null,
            "email": "grace@example.com",
            "phone": null,
            "taxIdentifiers": null
          }
        },
        "ownership": {
          "relationshipExternalId": "ownership-example-002",
          "ownershipPercent": 40
        },
        "roles": [
          {
            "relationshipExternalId": "director-example-002",
            "role": "DIRECTOR"
          }
        ]
      }
    ],
    "dossier": {
      "sourceVersion": "provider-kyb-example-001",
      "sourceEffectiveAt": "2026-08-21T12:00:00Z",
      "documents": [],
      "providerEvidence": []
    }
  }'
```

Use explicit `null` when your source states that a nullable fact is unavailable. Do not remove
required keys or invent placeholder values. The API Reference is authoritative for the complete
field constraints.

## Read the response

The response gives the business and each UBO a distinct stable Customer `resourceId`:

```json theme={null}
{
  "business": {
    "outcome": "CREATED",
    "resourceId": "business_customer_resource_example_01",
    "versionId": "business_customer_version_example_01",
    "currentVersionId": "business_customer_version_example_01"
  },
  "beneficialOwners": [
    {
      "customer": {
        "outcome": "CREATED",
        "resourceId": "beneficial_owner_customer_01_resource_example_01",
        "versionId": "beneficial_owner_customer_01_version_example_01",
        "currentVersionId": "beneficial_owner_customer_01_version_example_01"
      },
      "ownership": {
        "resourceId": "ownership_relationship_01_resource_example_01",
        "versionId": "ownership_relationship_01_version_example_01",
        "currentVersionId": "ownership_relationship_01_version_example_01"
      },
      "roles": [
        {
          "resourceId": "control_relationship_01_resource_example_01",
          "versionId": "control_relationship_01_version_example_01",
          "currentVersionId": "control_relationship_01_version_example_01",
          "role": "CONTROL_PERSON"
        }
      ]
    },
    {
      "customer": {
        "outcome": "CREATED",
        "resourceId": "beneficial_owner_customer_02_resource_example_01",
        "versionId": "beneficial_owner_customer_02_version_example_01",
        "currentVersionId": "beneficial_owner_customer_02_version_example_01"
      },
      "ownership": {
        "resourceId": "ownership_relationship_02_resource_example_01",
        "versionId": "ownership_relationship_02_version_example_01",
        "currentVersionId": "ownership_relationship_02_version_example_01"
      },
      "roles": [
        {
          "resourceId": "director_relationship_02_resource_example_01",
          "versionId": "director_relationship_02_version_example_01",
          "currentVersionId": "director_relationship_02_version_example_01",
          "role": "DIRECTOR"
        }
      ]
    }
  ],
  "dossier": {
    "dossierId": "kyb_dossier_example_01",
    "customerVersionId": "business_customer_version_example_01",
    "workflowRunId": "workflow_run_example_01",
    "caseId": "case_example_01",
    "evaluationStatus": "QUEUED",
    "statusUrl": "/api/cases/case_example_01",
    "observedAt": "2026-08-21T12:00:01Z"
  }
}
```

Store all three Customer `resourceId` values. The UBO IDs can be referenced anywhere another
individual Customer can be referenced.

## How the API ingests this request

The supported integration boundary is the HTTP API. Karsa processes this request as follows:

1. Fastify authenticates the bearer credential and binds the request to its tenant and source.
2. The request schema validates the business, every complete `INDIVIDUAL` UBO profile, every
   relationship, and the same complete dossier contract used by the standalone KYB endpoint before
   any records are committed.
3. The idempotency receipt and one database transaction cover the complete onboarding operation.
4. The business and every UBO pass through the same Customer ingestion path used by
   `POST /api/ingestion/customers`.
5. Karsa creates the ownership and role relationships, pins the dossier to the returned business
   CustomerVersion and the stated business-time cutoff, creates its durable Case, and queues the
   internal KYB workflow.
6. The transaction commits and the API returns all Customer, relationship, dossier, Case, and
   internal workflow identifiers.

Our local acceptance test follows this exact path: it sends the authenticated request to the local
Fastify server over loopback HTTP, retries it with the same idempotency key, and only then uses
read-only PostgreSQL queries to verify the committed rows. It does not insert test records directly
into the database.

## What Karsa commits

Karsa validates the whole request before committing it. One successful request writes:

1. one stable business Customer and its immutable CustomerVersion;
2. one stable individual Customer and CustomerVersion for every UBO;
3. versioned beneficial-ownership and role relationships from each UBO Customer to the business
   Customer;
4. one immutable KYB dossier pinned to the business CustomerVersion;
5. one durable Case in `PROCESSING`; and
6. one queued internal KYB workflow run.

These writes are atomic. If Customer validation, a relationship, the dossier, or the KYB runtime
fails, none of the Customers or relationships from that request are committed.

## Retry safely

After a timeout or ambiguous network response, send the exact same body with the same
`Idempotency-Key`. Karsa returns the original response instead of creating duplicate Customers,
relationships, dossiers, Cases, or workflow runs.

Use a new idempotency key only for a new logical submission. Keep each source `externalId` stable so
later observations append immutable versions to the same Customer identity.

## Use separate requests when you need more control

You can instead create the business and each person separately through
`POST /api/ingestion/customers`, then submit the KYB dossier. That pattern is useful when the people
already exist as Customers or arrive at different times. Use the atomic onboarding route when your
system needs certainty that the business and every supplied UBO were accepted together.

## Related

* [Customers](/concepts/customers): understand business and individual Customer records
* [KYB dossiers](/guides/kyb-dossiers): submit evidence for an existing business CustomerVersion
* [Upload flow](/guides/upload-flow): connect documents, accounts, transactions, and outcomes
* **API Reference → Customers → Create a business and its UBO customers**: inspect every field and error


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.