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

> ## Agent Instructions
> Authenticate with the raw API key in the Authorization header. Never add a Bearer prefix.
> Use the sandbox base URL https://api.test.doola.com and a dk_test_ key in examples unless the user asks for production.
> Send an Idempotency-Key header on POST /v1/partner/customers, POST /v1/partner/companies, POST /v1/partner/companies/import, POST /v1/partner/companies/{companyId}/services and POST /v1/partner/companies/{companyId}/documents. No other endpoint takes one.
> Every response is wrapped as {payload, error}. Read fields from payload and branch on error.code, never on the message.
> Drive formation progress from webhooks plus a reconciliation sweep, never a polling loop.
> Embedded SDK (@doola/js): the dk_ key stays on the server and mints customer sessions; the browser gets only a pk_ key and the session. onFormed carries only companyId and can fire more than once. Before charging, read the company with the dk_ key, require formationSubmissionStatus AWAITING_PAYMENT, check the owner's email, and price on the server. Then POST payment-confirmed, which answers 200 with an empty body, and replace the SDK element with a new doola.create() (never append a second one).
> The doola Formation MCP server at https://mcp.doola.com is for founders forming their own company with their own doola account. Partners integrate the Partner API or the Embedded SDK. The /mcp endpoint on this docs site only searches these docs.
> For a full capability summary with the rules above, read https://docs.doola.com/skill.md.

# Import a company

> Bring a company your customer already formed outside doola into your account, upload its documents, and follow doola's review until it is verified.

Your customer may already own a US company that someone else formed. Import it to manage it through doola: file its annual reports and order an EIN for it. An import files nothing. doola records the company as the state has it on file, reviews it against its documents, and marks it verified.

## The flow

```mermaid theme={null}
sequenceDiagram
    participant P as Your backend
    participant D as doola API

    P->>D: POST /v1/partner/companies/import
    D-->>P: 201, importStatus AWAITING_DOCUMENTS
    D-->>P: webhook company_imported
    P->>D: POST /v1/partner/companies/{companyId}/documents (each required document)
    Note over D: All required documents on file
    D->>D: importStatus IN_REVIEW, doola reviews the import
    opt doola needs a correction
        D-->>P: webhook company_import_info_required
        P->>D: Resolve with the corrections
    end
    D->>D: importStatus VERIFIED
    D-->>P: webhook company_import_verified
    Note over P,D: Annual filing opens, an ordered EIN starts
```

1. [Import the company](#importing-the-company). It starts at `importStatus: AWAITING_DOCUMENTS`.
2. [Upload the documents](#uploading-the-documents) listed in `documentRequirements`. Once every one is on file, the import moves to `IN_REVIEW`.
3. doola [reviews the import](#review-and-verification). If something needs correcting, doola raises a [required action](https://docs.doola.com/api/required-actions/import.md) and you answer it.
4. doola verifies the import: `importStatus` becomes `VERIFIED` and `company_import_verified` fires.

## Importing the company

```bash theme={null}
curl https://api.test.doola.com/v1/partner/companies/import \
  -X POST \
  -H "Authorization: dk_test_your_api_key_here" \
  -H "Idempotency-Key: import-acme-wy-2023-001234567" \
  -H "Content-Type: application/json" \
  -d '{
    "doolaCustomerId": "3LPpOw06qQ81ivtI9Rt5RofhWGO",
    "entityType": "LLC",
    "managementType": "MEMBER_MANAGED",
    "state": "WY",
    "nameOptions": [{ "name": "Acme Robotics", "entityTypeEnding": "LLC" }],
    "formationFilingDate": "2023-04-12",
    "formationFilingNumber": "2023-001234567",
    "ein": "99-1234567",
    "industry": "Personal services",
    "description": "A software company",
    "responsibleParty": {
      "legalFirstName": "John",
      "legalLastName": "Doe",
      "email": "john.doe@example.com",
      "ssn": "123-45-6789",
      "address": { "line1": "123 Main St", "city": "Cheyenne", "state": "WY", "postalCode": "82001", "country": "USA", "phone": "+12125550100" }
    },
    "addresses": [
      { "provider": "customer", "type": "business", "address": { "line1": "123 Main St", "city": "Cheyenne", "state": "WY", "postalCode": "82001", "country": "USA" } },
      { "provider": "customer", "type": "mailing",  "address": { "line1": "123 Main St", "city": "Cheyenne", "state": "WY", "postalCode": "82001", "country": "USA" } }
    ],
    "members": [
      {
        "isNaturalPerson": true,
        "legalFirstName": "Jane",
        "legalLastName": "Doe",
        "ownershipPercent": 100,
        "ssn": "987-65-4321",
        "address": { "line1": "123 Main St", "city": "Cheyenne", "state": "WY", "postalCode": "82001", "country": "USA", "phone": "+12125550100" }
      }
    ],
    "registeredAgent": {
      "name": "Wyoming Registered Agents LLC",
      "address": { "line1": "30 N Gould St", "city": "Sheridan", "state": "WY", "postalCode": "82801", "country": "USA" }
    }
  }'
```

`Idempotency-Key` is required, as on [Create a company](https://docs.doola.com/api/api-reference/companies/create-a-company.md). Retrying with the same key and body returns the first response. A failed import releases the key, so fix the request and retry with the same key.

### Fields

Send the company as the state has it on file today. Fields shared with company creation follow the same rules: see [Domain objects](https://docs.doola.com/api/domain-objects.md).

| Field | Required | Notes |
| - | - | - |
| `doolaCustomerId` | Yes | The customer who owns the company |
| `entityType` | Yes | `LLC` or `CCorp`. Unlike on create, there is no default |
| `managementType` | LLC only | `MEMBER_MANAGED` or `MANAGER_MANAGED`. Required for an LLC, omitted for a CCorp |
| `state` | Yes | Two-letter state of formation, for example `WY` |
| `nameOptions` | Yes | Exactly one entry: the legal name. `name` without the ending, and an `entityTypeEnding` that belongs to `entityType`. `position` is ignored |
| `formationFilingDate` | Yes | The date the state formed the company, `YYYY-MM-DD`. Cannot be in the future |
| `formationFilingNumber` | Yes | The filing number the state assigned. At most 255 characters. A state and filing number name one company, so an import of a company already registered with doola is refused, see `409 E_COMPANY_ALREADY_REGISTERED` below |
| `ein` | No | The company's EIN, as `NN-NNNNNNN`. Send it when the company has one |
| `industry` | Yes | An exact label from the NAICS reference. See [Choosing an industry](https://docs.doola.com/api/naics-industry.md) |
| `description` | Yes | What the business does, up to 50 characters |
| `responsibleParty` | Yes | Same shape and rules as on create |
| `addresses` | Yes | Exactly one `business` and one `mailing` entry, both with `provider: customer`. doola is not this company's registered agent, so `registeredAgent` is refused here |
| `members` | LLC only | The owners, with ownership totalling 100. Required for an LLC, whether it is member-managed or manager-managed |
| `managers` | Manager-managed LLC only | At least one when `managementType` is `MANAGER_MANAGED`, none otherwise. See [Manager](https://docs.doola.com/api/domain-objects.md#manager) |
| `executiveMembers` | CCorp only | At least one President, Secretary, Treasurer and Director |
| `ccorpValuation` | CCorp only | Authorized shares and par value |
| `registeredAgent` | Yes | The company's current registered agent: `name` and a complete `address`. Each value at most 255 characters |

### Response

`201` with the company. An imported company differs from one doola forms in a few fields:

```json theme={null}
{
  "payload": {
    "doolaCompanyId": "3LPpP4O7uS6YmXuyA6nX8PQImw7",
    "doolaCustomerId": "3LPpOw06qQ81ivtI9Rt5RofhWGO",
    "entityType": "LLC",
    "managementType": "MEMBER_MANAGED",
    "state": "WY",
    "nameOptions": [
      { "id": "3LPpP7q1rJXhhTiuxcTYp9OljAp", "name": "Acme Robotics", "entityTypeEnding": "LLC", "position": 0 }
    ],
    "industry": "Personal services",
    "description": "A software company",
    "responsibleParty": {
      "id": "3LPpP1kgf0dCICz1H5qmKxeo6hQ",
      "legalFirstName": "John",
      "legalLastName": "Doe",
      "email": "john.doe@example.com",
      "ssn": "****-**-6789",
      "address": { "line1": "123 Main St", "line2": null, "city": "Cheyenne", "state": "WY", "postalCode": "82001", "country": "USA", "phone": "+12125550100" }
    },
    "addresses": [
      { "provider": "customer", "type": "business", "address": { "line1": "123 Main St", "line2": null, "city": "Cheyenne", "state": "WY", "postalCode": "82001", "country": "USA", "phone": null } },
      { "provider": "customer", "type": "mailing", "address": { "line1": "123 Main St", "line2": null, "city": "Cheyenne", "state": "WY", "postalCode": "82001", "country": "USA", "phone": null } }
    ],
    "members": [
      {
        "id": "3LPpP25FqNijsYukkx6UzrUmX4P",
        "isNaturalPerson": true,
        "legalFirstName": "Jane",
        "legalLastName": "Doe",
        "contactFullName": null,
        "ownershipPercent": 100,
        "ssn": "****-**-4321",
        "address": { "line1": "123 Main St", "line2": null, "city": "Cheyenne", "state": "WY", "postalCode": "82001", "country": "USA", "phone": "+12125550100" }
      }
    ],
    "adminNotes": [],
    "formationSubmissionStatus": "COMPLETED",
    "ein": "99-1234567",
    "formationFilingDate": "2023-04-12",
    "formationFilingNumber": "2023-001234567",
    "services": [
      { "name": "AiCofounder", "variant": "NONE", "status": "Active", "subStatus": null }
    ],
    "signatureRequirements": [],
    "origin": "IMPORTED",
    "importStatus": "AWAITING_DOCUMENTS",
    "documentRequirements": [
      { "documentType": "ArticlesOfOrganization", "status": "missing" },
      { "documentType": "EinLetter", "status": "missing" }
    ]
  },
  "error": null
}
```

| Field | On an imported company |
| - | - |
| `origin` | `IMPORTED`. A company doola formed reads `DOOLA` |
| `importStatus` | Where the import stands: see [Import statuses](#import-statuses). Absent on a company doola formed |
| `documentRequirements` | The documents to upload and whether each is on file. Absent on a company doola formed |
| `formationSubmissionStatus` | `COMPLETED`: the state already formed the company |
| `services` | `AiCofounder` only. No `Formation`, `RegisteredAgent` or `EinCreation` work starts. [Add an EIN](#ordering-an-ein) if the company needs one |
| `managementType`, `managers` | As you sent them. `managers` is present only for a manager-managed LLC, with masked SSNs |

The registered agent you sent is stored with the import but not returned on the company.

`company_imported` fires once the import is saved. No formation webhooks fire for an imported company.

## Uploading the documents

`documentRequirements` lists what doola needs before it can review the import:

| documentType | Required when |
| - | - |
| `ArticlesOfOrganization` | Always. For a C-Corp, upload its Articles or Certificate of Incorporation under this type |
| `EinLetter` | You sent an `ein`. Upload the IRS CP 575 or 147C letter |

Upload each one as `multipart/form-data`, with the file in `file` and its type in `documentType`:

```bash theme={null}
curl https://api.test.doola.com/v1/partner/companies/{companyId}/documents \
  -X POST \
  -H "Authorization: dk_test_your_api_key_here" \
  -H "Idempotency-Key: upload-articles-3LPpP4O7uS6YmXuyA6nX8PQImw7" \
  -F "documentType=ArticlesOfOrganization" \
  -F "file=@articles.pdf;type=application/pdf"
```

The response is `201` with the stored [document](https://docs.doola.com/api/domain-objects.md#document):

```json theme={null}
{
  "payload": {
    "id": "3LbB2tE5fG8hI1jK4lM7nO0pQ3r",
    "companyId": "3LPpP4O7uS6YmXuyA6nX8PQImw7",
    "name": "articles.pdf",
    "contentType": "application/pdf",
    "documentType": "ArticlesOfOrganization",
    "createdAt": "2026-11-03T10:02:11",
    "lastModified": "2026-11-03T10:02:11"
  },
  "error": null
}
```

* **PDF only, up to 10MB**, with a file name of at most 255 characters. doola checks the file's contents, not the declared content type.
* **`documentType`** is one of `ArticlesOfOrganization`, `EinLetter`, `OperatingAgreement`, `CorporateBylaws` or `OTHER`.
* **`Idempotency-Key` is required.** Retrying the same file with the same key returns the first upload instead of storing a second copy.
* The document then appears in the company's document list and downloads by its `id`. Every type except `OTHER` fires its `document_*_uploaded` webhook, as when doola stores a document.

You can upload to any company you own, not only imported ones. On a company doola formed, an upload has the same effect as doola receiving that document, so upload there only when doola asks you to.

Once the last required document is on file, the import moves to `IN_REVIEW`. This happens shortly after the upload, not inside the upload call, so read the company again rather than expecting the upload response to show it.

### If documents are missing

doola checks once a day for imports still at `AWAITING_DOCUMENTS` a day after they were created. For each one it raises a single `IMPORT_DOCUMENTS_MISSING` [required action](https://docs.doola.com/api/required-actions/import.md#missing-documents), announced by the `company_import_documents_missing` webhook. Upload the missing documents and the action closes on its own. Nothing else happens if they never arrive: the import stays at `AWAITING_DOCUMENTS` and annual filing stays closed.

### Upload errors

| HTTP | Code | When |
| - | - | - |
| 400 | `E_DOCUMENT_TYPE_INVALID` | `documentType` is not one of the allowed values |
| 400 | `E_DOCUMENT_FILE_INVALID` | The file is not a PDF, or its name is missing or longer than 255 characters |
| 400 | `E_FILE_TOO_LARGE` | The file is larger than 10MB |
| 400 | `E_REQUEST_BODY_INVALID` | The `file` or `documentType` part is missing |
| 400 | `E_IDEMPOTENCY_KEY_REQUIRED` | The `Idempotency-Key` header is missing |
| 404 | `E_COMPANY_NOT_FOUND` | No company with this ID belongs to your tenant |
| 409 | `E_IDEMPOTENCY_KEY_REUSED` | The key was already used for a different upload |
| 409 | `E_IDEMPOTENCY_IN_PROGRESS` | An upload with the same key is still running |

## Review and verification

Once the documents are on file, doola checks them against the facts you sent.

* **Everything matches.** doola verifies the import. `importStatus` becomes `VERIFIED` and `company_import_verified` fires.
* **Something needs correcting.** doola raises an `IMPORT_INFO_REQUIRED` [required action](https://docs.doola.com/api/required-actions/import.md#more-information-needed) that names the fields and documents it needs. You send the corrections, doola applies them to the company, and the review continues.

There is no rejection: doola resolves every problem through a required action.

### Import statuses

| `importStatus` | Meaning |
| - | - |
| `AWAITING_DOCUMENTS` | Not every document in `documentRequirements` is on file yet |
| `IN_REVIEW` | The documents are on file and doola is reviewing the import |
| `VERIFIED` | doola verified the import. Annual filing is open and an ordered EIN starts |

A verified import can go back to `IN_REVIEW` if doola needs to check it again. No webhook announces that move, so read `importStatus` in your [reconciliation](https://docs.doola.com/api/webhooks.md#reconciliation) sweep. Annual filing closes again until the import is verified again. Work that already started, such as an EIN application, carries on.

### What waits for verification

* **Annual filing.** Submitting or confirming an [annual report](https://docs.doola.com/api/compliance/annual-report-filing.md) for an imported company answers `409 E_IMPORT_NOT_VERIFIED` until `importStatus` is `VERIFIED`. The requirement and fee reads work at any time.
* **The EIN.** See [Ordering an EIN](#ordering-an-ein).

## Ordering an EIN

An imported company without an EIN can order one with [Add a service](https://docs.doola.com/api/add-services.md). You can order it at any point after the import:

* **Before verification,** the EIN waits at `NotStarted` / `WaitingForFilingServiceToComplete` and starts when doola verifies the import.
* **After verification,** it starts right away.

A company imported with an `ein` already has one, so ordering another answers `409 E_EIN_ALREADY_ISSUED`.

## Listing imported companies

[List companies](https://docs.doola.com/api/api-reference/companies/list-companies.md) returns `origin` on every company and `importStatus` on imported ones, and takes two filters:

```bash theme={null}
curl "https://api.test.doola.com/v1/partner/companies?origin=IMPORTED&importStatus=IN_REVIEW" \
  -H "Authorization: dk_test_your_api_key_here"
```

| Parameter | Values |
| - | - |
| `origin` | `DOOLA` for companies doola formed, `IMPORTED` for imported ones |
| `importStatus` | `AWAITING_DOCUMENTS`, `IN_REVIEW` or `VERIFIED` |

Any other value answers `400 E_VALIDATION_FAILED`, with the detail on `error.fields.origin` or `error.fields.importStatus`.

## Webhooks

| Event | Fires when |
| - | - |
| `company_imported` | The import is saved |
| `company_import_documents_missing` | Documents are still missing a day after the import. Announces `IMPORT_DOCUMENTS_MISSING` |
| `company_import_info_required` | doola needs a correction. Announces `IMPORT_INFO_REQUIRED` |
| `company_import_verified` | doola verified the import |

`company_imported` and `company_import_verified` carry `doolaCompanyId` only: read the company for the rest. The two required-action events carry the [required action](https://docs.doola.com/api/required-actions.md#how-doola-tells-you) payload. See [Webhooks](https://docs.doola.com/api/webhooks.md#events) for examples.

## Errors

Branch on `error.code`, never on the message. A field error answers `400 E_VALIDATION_FAILED` with the field's own code under `error.fields`:

| Field | Code under `error.fields` | When |
| - | - | - |
| `doolaCustomerId` | `E_CUSTOMER_ID_REQUIRED` | Missing or not a valid ID |
| `entityType` | `E_ENTITY_TYPE_INVALID` | Missing, or not `LLC` or `CCorp` |
| `managementType` | `E_MANAGEMENT_TYPE_INVALID` | Missing or unknown on an LLC, or sent for a CCorp |
| `managers` | `E_MANAGERS_INVALID` | None for a manager-managed LLC, any for a member-managed LLC, or a manager breaking the responsible party's rules or longer than 255 characters in a field |
| `state` | `E_STATE_INVALID` | Missing, blank or not a US state code |
| `nameOptions` | `E_NAME_OPTIONS_INVALID` | Not exactly one entry, or an ending that does not belong to `entityType` |
| `formationFilingDate` | `E_FORMATION_FILING_DATE_INVALID` | Missing or in the future |
| `formationFilingNumber` | `E_FORMATION_FILING_NUMBER_INVALID` | Missing, blank or longer than 255 characters |
| `ein` | `E_EIN_INVALID` | Not formatted as `NN-NNNNNNN` |
| `industry` | `E_INDUSTRY_INVALID` | Missing, or not a label from the NAICS reference |
| `description` | `E_DESCRIPTION_INVALID` | Missing or longer than 50 characters |
| `responsibleParty` | `E_RESPONSIBLE_PARTY_INVALID` | Missing, or breaking the responsible party rules |
| `addresses` | `E_ADDRESSES_INVALID` | Not one `business` and one `mailing` entry, or a `provider` other than `customer` |
| `members` | `E_MEMBERS_INVALID` | Missing on an LLC, or ownership not totalling 100 |
| `executiveMembers` | `E_EXECUTIVE_MEMBERS_INVALID` | Missing on a CCorp, or a required role missing |
| `ccorpValuation` | `E_CCORP_VALUATION_INVALID` | Missing on a CCorp, or not above 0 |
| `registeredAgent` | `E_REGISTERED_AGENT_INVALID` | Missing, no `name`, an incomplete address, or a value longer than 255 characters |

The other errors:

| HTTP | Code | When |
| - | - | - |
| 400 | `E_IDEMPOTENCY_KEY_REQUIRED` | The `Idempotency-Key` header is missing |
| 404 | `E_CUSTOMER_NOT_FOUND` | No customer with this `doolaCustomerId` belongs to your tenant |
| 409 | `E_COMPANY_ALREADY_REGISTERED` | A company with this `state` and `formationFilingNumber` is already registered with doola. One of your own companies always counts, whatever stage its import is at, and the message names its `doolaCompanyId`, so you can recover a lost response. Otherwise the message names none. An archived company never counts |
| 409 | `E_IDEMPOTENCY_KEY_REUSED` | The key was used before with a different body |
| 409 | `E_IDEMPOTENCY_IN_PROGRESS` | A request with the same key is still running |

A refused import stores nothing.


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