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

# Build an incorporation flow

> Implement a customer incorporation workflow from customer creation through final submit for review.

This guide shows the recommended implementation sequence for a customer incorporation flow. Use [Core concepts](/engine/incorporation/partner-incorporation-core-concepts) for the object model and the endpoint reference pages for exact request and response schemas.

## Minimum happy path

1. Create or find the customer record and save `customerId`.
2. Read the jurisdiction catalog, choose `country` plus `countryOptions`, and fetch the requirements schema.
3. Create the incorporation and save `incorporation.id`.
4. Create or reuse required Commenda OS records, such as people, business entities, locations, and documents.
5. Register participants and upsert required participant documents.
6. Submit incorporation-level intake fields, then read validation until `incorporationValidation.isComplete` is `true`.
7. Submit for review, then track status and active issues.

## Detailed implementation sequence

| Phase                | Developer question                      | What to do                                                                                                                                                                                                                           |
| -------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Setup                | What record owns the work?              | Create a customer and save `customerId`.                                                                                                                                                                                             |
| Jurisdiction catalog | What can I incorporate?                 | Read the jurisdiction catalog and choose `country` plus `countryOptions`.                                                                                                                                                            |
| Requirements         | What data do I need?                    | Fetch the requirements schema for the selected jurisdiction/type.                                                                                                                                                                    |
| Progress setup       | How will progress display?              | Fetch the status workflow for the selected incorporation type.                                                                                                                                                                       |
| Incorporation        | How do I start the incorporation?       | Create the incorporation and save `incorporation.id`.                                                                                                                                                                                |
| Add customer records | What reusable records must exist?       | Create or reuse Commenda OS people, business entities, and locations.                                                                                                                                                                |
| Participants         | Who is involved?                        | Register reusable people/entities as directors and shareholders.                                                                                                                                                                     |
| Documents            | How do I attach files?                  | Follow [Upload files for incorporation](/engine/incorporation/partner-incorporation-document-upload-flow): upload customer files with multipart form data to obtain `fileId`, then upsert each file as a typed participant document. |
| Intake               | What non-participant fields are needed? | Submit partial or complete intake updates.                                                                                                                                                                                           |
| Submit for review    | How do I hand it to Commenda?           | Read validation, call the submit endpoint when `incorporationValidation.isComplete` is `true`, then track `GET /partner/incorporation/{incorporationId}/status`.                                                                     |
| Issues               | What if Commenda needs corrections?     | Read active/resolved issues, update the underlying intake/participants/documents, and optionally receive issue webhooks. Active issues set top-level status to `BLOCKED`.                                                            |

## IDs you'll save

| ID                           | Returned by                                                          | Used in                                                                 | Notes                                                                                                                                                                                               |
| ---------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customerId`                 | `POST /partner/customers` as `customer.id`                           | Create incorporation; Commenda OS customer-scoped endpoints             | Numeric customer id.                                                                                                                                                                                |
| `incorporation.id`           | Create/list/get incorporation                                        | Participants, documents, intake, direct read route                      | Use this as `{incorporationId}`.                                                                                                                                                                    |
| `businessEntityId`           | Create/list/get incorporation                                        | Commenda OS business-entity endpoints for the entity being incorporated | Target business entity id for the entity being incorporated. For new Partner API incorporations, this is returned on create. It is different from corporate shareholder `businessEntity.id` values. |
| Person `id`                  | `POST /partner/commenda-os/customers/{customerId}/persons`           | Participant `resource.resourceId`                                       | Send as a number, for example `12`.                                                                                                                                                                 |
| Business entity `id`         | `POST /partner/commenda-os/customers/{customerId}/business-entities` | Corporate participant `resource.resourceId`                             | Send as a number, for example `44`.                                                                                                                                                                 |
| Participant `id`             | Register participant                                                 | Update participant roles; upsert participant documents                  | Incorporation-scoped participant id.                                                                                                                                                                |
| `fileId`                     | `POST /partner/commenda-os/customers/{customerId}/files`             | Upsert participant document                                             | Customer-scoped file id returned after multipart upload.                                                                                                                                            |
| `documentId`                 | Upsert participant document                                          | Read/list participant state                                             | Incorporation-scoped typed document link.                                                                                                                                                           |
| `statusWorkflow.stages[].id` | Status workflow or incorporation status                              | Progress UI and issue-stage correlation                                 | Stage ids come from the active workflow definition.                                                                                                                                                 |
| `issue.id`                   | List issues or issue webhook payload                                 | Dedupe issue webhooks; correlate correction requests                    | One issue row represents one remediation episode.                                                                                                                                                   |
| `webhookSubscription.id`     | Create/list webhook subscriptions                                    | Read or delete webhook subscription                                     | Partner-level subscription, not scoped to one customer.                                                                                                                                             |

## Happy path: Singapore private limited company

This example uses one individual as both director and shareholder.

```text theme={null}
customerId = 77
personId = 12
incorporationId = 0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91
participantId = participant_123
passportFileId = 456
utilityBillFileId = 789
```

### 1. Create the customer

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/customers' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "email": "owner@example.com",
    "name": "Acme Singapore"
  }'
```

Save the returned `customerId`.

### 2. Choose jurisdiction and fetch requirements

```bash theme={null}
curl --request GET \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/jurisdiction-catalog' \
  --header 'x-api-key: <partner_api_key>'
```

For Singapore private limited company requirements:

```bash theme={null}
curl --request GET \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/requirements?country=SG&countryOptions[corporationType]=PRIVATE_LIMITED_COMPANY' \
  --header 'x-api-key: <partner_api_key>'
```

For the public progress stages:

```bash theme={null}
curl --request GET \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/status-workflow?country=SG&countryOptions[corporationType]=PRIVATE_LIMITED_COMPANY' \
  --header 'x-api-key: <partner_api_key>'
```

Render your UI from the returned requirements schema, but treat the server response as informational. Commenda validates every intake update against the current server-side requirements.

### 3. Create the incorporation

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/customers/77/incorporations' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "country": "SG",
    "countryOptions": {
      "corporationType": "PRIVATE_LIMITED_COMPANY"
    }
  }'
```

Save `incorporation.id`. The initial `incorporationStatus` is usually `AWAITING_CUSTOMER_INPUT`.

### 4. Create or reuse a person

If the director/shareholder does not already exist in Commenda OS, create a reusable person:

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/commenda-os/customers/77/persons' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "firstName": "Jane",
    "lastName": "Founder",
    "email": "jane@example.com",
    "dateOfBirth": "1990-04-18",
    "countryOfCitizenship": "SG"
  }'
```

Save the returned person `id`. When you register the participant, send it as a number in `resource.resourceId`.

### 5. Register participants and roles

For Singapore private limited companies, use this role/type matrix:

| Participant scenario   | `participantType` | `resource.resourceType` | Supported role payload                                     |
| ---------------------- | ----------------- | ----------------------- | ---------------------------------------------------------- |
| Individual director    | `INDIVIDUAL`      | `KEY_PERSON`            | `{ "role": "DIRECTOR" }`                                   |
| Individual shareholder | `INDIVIDUAL`      | `KEY_PERSON`            | `{ "role": "SHAREHOLDER", "ownershipPercentage": number }` |
| Corporate shareholder  | `CORPORATE`       | `BUSINESS_ENTITY`       | `{ "role": "SHAREHOLDER", "ownershipPercentage": number }` |
| Corporate director     | `CORPORATE`       | `BUSINESS_ENTITY`       | Not supported.                                             |

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/participants' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "participantType": "INDIVIDUAL",
    "resource": {
      "resourceType": "KEY_PERSON",
      "resourceId": 12
    },
    "roles": [
      { "role": "DIRECTOR" },
      {
        "role": "SHAREHOLDER",
        "ownershipPercentage": 40
      }
    ]
  }'
```

For Singapore private limited companies, registered shareholder participant `ownershipPercentage` values must total 100. Participant writes that would make shareholder ownership exceed 100 are rejected; totals below 100 are accepted while the customer is still entering shareholders, but validation remains incomplete until the total equals 100.

If the shareholder is a company, create or reuse a Commenda OS business entity first:

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/commenda-os/customers/77/business-entities' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "incorporationCountry": "SG",
    "legalName": "Acme Holdings Pte. Ltd.",
    "jurisdiction": "JUR_SG__GENERAL",
    "corporationType": "PteLtd"
  }'
```

Then register it as a corporate shareholder:

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/participants' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "participantType": "CORPORATE",
    "resource": {
      "resourceType": "BUSINESS_ENTITY",
      "resourceId": 44
    },
    "roles": [
      {
        "role": "SHAREHOLDER",
        "ownershipPercentage": 60
      }
    ]
  }'
```

To correct roles or ownership after registration, send the full desired roles array for that participant:

```bash theme={null}
curl --request PATCH \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/participants/participant_123' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "roles": [
      { "role": "DIRECTOR" },
      {
        "role": "SHAREHOLDER",
        "ownershipPercentage": 40
      }
    ]
  }'
```

### 6. Upload and upsert documents

Upload each source document as multipart form data to create a reusable customer file. Upserting the participant document assigns that file to the participant document slot without changing the reusable file.

<Info>
  The multipart upload endpoint lives in the Commenda OS Partner API because files are reusable customer resources. For a shorter file-upload walkthrough, see [Upload files for incorporation](/engine/incorporation/partner-incorporation-document-upload-flow).
</Info>

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/commenda-os/customers/77/files' \
  --header 'x-api-key: <partner_api_key>' \
  --form 'file=@./passport.pdf;type=application/pdf'

curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/commenda-os/customers/77/files' \
  --header 'x-api-key: <partner_api_key>' \
  --form 'file=@./utility-bill.pdf;type=application/pdf'
```

Then upsert each file as a typed participant document:

```bash theme={null}
curl --request PUT \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/participants/participant_123/documents/PASSPORT_SCAN' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "fileId": 456
  }'

curl --request PUT \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/participants/participant_123/documents/UTILITY_BILL' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "fileId": 789
  }'
```

For Singapore private limited companies, every individual director and individual shareholder needs both `PASSPORT_SCAN` and `UTILITY_BILL`.

### 7. Submit intake data

Intake updates are merge-based and may be partial. Use this endpoint for incorporation details such as company name options and registered office address choice.

```bash theme={null}
curl --request PATCH \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/intake' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "requirements": {
      "companyNameOptions": [
        { "name": "Acme SG Pte. Ltd." },
        { "name": "Acme Asia Pte. Ltd." },
        { "name": "Acme Global Pte. Ltd." }
      ],
      "registeredOfficeAddressSource": "commendaService"
    }
  }'
```

### 8. Check readiness to submit

```bash theme={null}
curl --request GET \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/validation' \
  --header 'x-api-key: <partner_api_key>'
```

Check `incorporationValidation.isComplete`. When it is `true`, the incorporation is eligible to submit for Commenda review.

### 9. Submit for review

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/submit' \
  --header 'x-api-key: <partner_api_key>'
```

The submit endpoint refreshes validation before accepting the handoff. If the incorporation is incomplete, it returns `400` with validation details. If it succeeds, Commenda begins review. The response usually returns `incorporationStatus: "IN_PROGRESS"` once review starts.

### 10. Track status

```bash theme={null}
curl --request GET \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/status' \
  --header 'x-api-key: <partner_api_key>'
```

Use this response for customer-facing progress. It includes the top-level `incorporationStatus`, ordered `stages`, and active `blockingIssueIds`.

## Requirements guide

| Requirements area                 | What it means                                                                       | How to use it                                                                                           |
| --------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `blocks`                          | UI-friendly groups of fields                                                        | Render fields by block if you want a guided UI.                                                         |
| `fields`                          | Incorporation-level data, such as company names or registered office address choice | Submit these keys under `requirements` in the intake endpoint.                                          |
| `required.when` / `appliesWhen`   | Rules that make a field required or applicable only when another field has a value  | Re-evaluate the UI when the controlling field changes. Non-applicable fields are ignored by validation. |
| `participantRequirements`         | Required roles such as director or shareholder                                      | Satisfy these by registering participants, not by sending arrays in intake.                             |
| `resourceFieldsByParticipantType` | Required fields on a participant's referenced Commenda OS resource                  | Complete these fields on the person or business entity before submitting.                               |
| `documentRequirements`            | Required typed documents for a participant role/type                                | Satisfy these by upserting customer files as participant documents.                                     |
| Version fields                    | Requirements version metadata                                                       | Store these in logs for debugging. Do not send them back or gate UI behavior on exact values.           |

Validation issues use paths that point to the missing or invalid requirement.

| Example path                                                         | Meaning                                                                         | Fix                                                                                                                                                                                               |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `companyNames.companyNameOptions`                                    | Company name options are missing or invalid                                     | Submit valid names through the intake endpoint as `requirements.companyNameOptions`.                                                                                                              |
| `registeredOfficeAddress.registeredOfficeAddressLocation`            | A partner-provided registered office address location is required               | Submit a Commenda OS location reference as `requirements.registeredOfficeAddressLocation` or choose `commendaService`, which means Commenda should provide the registered office address service. |
| `registeredOfficeAddress.registeredOfficeAddressLocation.resourceId` | The referenced registered office address location is missing or inaccessible    | Use a Commenda OS location for the same customer record as the incorporation, create or update the location through the Commenda OS Partner API, or choose `commendaService`.                     |
| `participants.directors`                                             | A required director participant is missing                                      | Register a person or business entity as a director.                                                                                                                                               |
| `participants.participant_123.resource.lastName`                     | A referenced participant resource is missing a required field                   | Update the reusable Commenda OS person or business entity, then reread the incorporation validation.                                                                                              |
| `participants.participant_123.resource.residentialAddress`           | A Singapore individual director or shareholder is missing a residential address | Update the Commenda OS person with `residentialAddress: { ... }`.                                                                                                                                 |
| `participants.participant_123.documents.PASSPORT_SCAN`               | A participant is missing a passport scan                                        | Upload a customer file, then upsert it as `PASSPORT_SCAN` for that participant.                                                                                                                   |

Fetch requirements after the user chooses a jurisdiction and corporation type. Render your UI from the requirements response, but decide readiness from validation returned by write/read calls because Commenda may update requirements over time.

## Completion semantics

`incorporationValidation.isComplete` means the current data satisfies the incorporation requirements and is eligible to submit. It does not by itself hand the incorporation to Commenda.

```text theme={null}
AWAITING_CUSTOMER_INPUT
  -> SUBMITTED    after POST /partner/incorporation/{incorporationId}/submit succeeds and before Commenda review work starts
  -> IN_PROGRESS  when Commenda review is in progress
  -> BLOCKED      when there are active partner-visible issues or review start/runtime failures
  -> COMPLETED    when the incorporation is complete
```

Correction requests are represented by read-only issues and surface as `BLOCKED` while active.

## Issues and correction requests

Commenda can create partner-visible issues when an incorporation needs correction or additional information.

```bash theme={null}
curl --request GET \
  --url 'https://api.prod.commenda.io/api/v1/partner/incorporation/0f9a8f5e-7f7c-4c1b-a60a-b1022f9d8c91/issues?status=ACTIVE' \
  --header 'x-api-key: <partner_api_key>'
```

Issues are read-only over the Partner API. To resolve them, fix the underlying data through the normal endpoints: update intake fields, register or update participants, upload and upsert replacement documents, or otherwise provide the requested information. Commenda resolves the issue after review.

Each issue row represents one remediation episode. Resolved incorporation issues are not reopened; if the same kind of correction is needed again later, Commenda creates a new issue with a new `issue.id`.

Active issues are also returned by the status endpoint as `blockingIssueIds`. If an issue scope includes `stageId`, that issue is attached to the matching stage's `blockedByIssueIds`; otherwise it blocks the first non-completed stage.

Issue codes include `MISSING_INFORMATION`, `INVALID_INFORMATION`, `DOCUMENT_REQUIRED`, `DOCUMENT_INVALID`, `COMPANY_NAME_REJECTED`, and `OTHER`. Commenda may add additional issue codes in future API updates.

## Issue webhooks

Subscribe to issue events if your integration should react without polling:

```bash theme={null}
curl --request POST \
  --url 'https://api.prod.commenda.io/api/v1/partner/webhook-subscriptions' \
  --header 'content-type: application/json' \
  --header 'x-api-key: <partner_api_key>' \
  --data '{
    "url": "https://partner.example.com/commenda/webhooks",
    "eventTypes": [
      "INCORPORATION_ISSUE_CREATED",
      "INCORPORATION_ISSUE_RESOLVED"
    ]
  }'
```

The create response includes the full signing secret once. Later reads return only `secretPreview`.

Webhook delivery is asynchronous and retried. Your endpoint should respond within 10 seconds and dedupe deliveries by `eventId`.

## Common errors and fixes

| Symptom                                        | Likely cause                                                                                                        | Fix                                                                                                                                                        |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` on create incorporation                  | Unsupported `countryOptions` or missing `countryOptions[corporationType]`                                           | Fetch the jurisdiction catalog and use an advertised option value.                                                                                         |
| `403` on customer-scoped call                  | The customer is not accessible to the partner API key                                                               | Check the `customerId` and API key.                                                                                                                        |
| `404` when registering participant             | The referenced person or business entity is not visible to the customer                                             | Create or fetch the resource through Commenda OS first.                                                                                                    |
| `400` when registering or updating participant | Shareholder role is missing `ownershipPercentage`, role/resource type is unsupported, or ownership would exceed 100 | Follow the selected incorporation requirements and adjust existing shareholder roles before adding more ownership.                                         |
| Incomplete validation for shareholders         | Ownership percentages do not total 100                                                                              | Update shareholder participant roles so totals equal 100.                                                                                                  |
| `400` when uploading customer file             | Missing file part, unsupported MIME type, or file too large                                                         | Send exactly one binary `file` part and a supported file type.                                                                                             |
| `404` when upserting document                  | The participant or file cannot be found                                                                             | Register the participant and upload the customer file first.                                                                                               |
| `400` when upserting document                  | File type is incompatible or the document kind is not required for this participant                                 | Use a required `documentKind` from the requirements response and a compatible customer file.                                                               |
| `400` for intake                               | Field value does not match the current incorporation requirements                                                   | Read `invalidRequirements` and update the field.                                                                                                           |
| `400` on submit                                | The current state is incomplete                                                                                     | Read the returned validation details or call `GET /partner/incorporation/{incorporationId}`, then fix missing/invalid requirements before retrying submit. |
