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

# Import Owners

> Add many owners in one call

Add several people to the owner directory at once, the way a CSV import does. Like [Create Owner](/api-reference/create-owner), this creates labels rather than accounts: no email is sent, no seat is used, and nobody gains access to your workspace.

The call is one statement whatever the row count, and it is safe to repeat. An address that already belongs to a live owner is skipped rather than duplicated, and an address belonging to an owner you previously deleted is revived rather than duplicated. The response tallies each case.

A row's name is only written when you send one, so re-importing a file with the emails but not the names will not blank the names already in the directory.

<Note>
  Unlike [Create Owner](/api-reference/create-owner), a name is not required here. `email` is the only field a row needs.
</Note>

## Body

<ParamField body="owners" type="array" required>
  Owners to add. Must contain at least one row, and no more than 2000.
</ParamField>

<ParamField body="owners[].email" type="string" required>
  Owner email address. Must be a valid address with a dotted domain: `dana@localhost` is rejected as a typo.
</ParamField>

<ParamField body="owners[].first_name" type="string">
  Owner first name.
</ParamField>

<ParamField body="owners[].last_name" type="string">
  Owner last name.
</ParamField>

## Partial success

A single bad row does not fail the import. A file of 200 rows with one malformed address adds 199 owners and reports the one it dropped. The request only fails outright when *every* row is unusable.

Rows counted as `skipped` are the ones that changed nothing:

* the email was missing or malformed
* the same email appeared earlier in the same request
* the email already belonged to a live owner

<Warning>
  A workspace holds at most 2000 owners. The cap is checked against the number of rows you send, not the number that turn out to be new, so re-importing a 400-row file into a directory that already holds those 400 owners is rejected with 400 even though it would have added nobody. Import in batches, or send only the rows you are adding.
</Warning>

## Response Fields

<ResponseField name="created" type="integer">
  Owners added to the directory.
</ResponseField>

<ResponseField name="revived" type="integer">
  Previously deleted owners restored. Reviving does not restore the opportunities the owner held before the delete, those stayed unassigned.
</ResponseField>

<ResponseField name="skipped" type="integer">
  Rows that changed nothing. See the list above.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.clustr-ai.com/api/public/owners/bulk" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "owners": [
        {"email": "sam@northstar.vc", "first_name": "Sam", "last_name": "Okafor"},
        {"email": "dana@acme.com", "first_name": "Dana", "last_name": "Reyes"},
        {"email": "priya@acme.com"}
      ]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "data": {
      "created": 2,
      "revived": 1,
      "skipped": 0
    }
  }
  ```

  ```json 400 theme={null}
  {
    "success": false,
    "error": {
      "message": "no valid owner rows: every email was missing or malformed",
      "code": "BAD_REQUEST"
    }
  }
  ```

  ```json 401 theme={null}
  {
    "success": false,
    "error": { "message": "Invalid or missing API key", "code": "UNAUTHORIZED" }
  }
  ```
</ResponseExample>
