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

# Importing users

> Bulk-create and update Iru Identity users from a CSV file, with required columns, validation rules, re-upload behavior, and when to use HR sync instead.

Import users in bulk from a **CSV** file when you need to add or update more than
a handful at once. Each imported user carries the **`csv`** source tag, and
re-uploading an updated file keeps them in sync. CSV import works well for initial
loads and one-off updates. If your HR system is the ongoing source of truth,
use [HR sync](#when-to-use-hr-sync-instead) instead.

You can also include [custom attributes](/en/identity/directory/schema#custom-attributes)
you have defined as columns in the file. In **Directory → Users**, click the arrow
next to **+ User**, then choose **Import via CSV**.

<Note>
  Re-upload whenever your list changes. You can combine CSV import with HR sync:
  many teams seed the directory with a CSV, then connect their HR system to keep
  it current. See [when to use HR sync](#when-to-use-hr-sync-instead).
</Note>

## File requirements

| Requirement                          | Detail                                                                                  |
| ------------------------------------ | --------------------------------------------------------------------------------------- |
| **Format**                           | Comma-separated values (`.csv`).                                                        |
| **Encoding**                         | UTF-8. A leading byte-order mark (as Excel adds) is accepted and ignored.               |
| **First row**                        | A **header row** naming the columns.                                                    |
| **Header names (core columns)**      | **Case-sensitive** and must match exactly (`externalId`, not `ExternalID`).             |
| **Header names (custom attributes)** | Use `categorySlug.attributeSlug`. Capitalization and spacing in the header are ignored. |
| **Maximum rows**                     | 50,000 data rows per upload.                                                            |
| **Maximum file size**                | 16 MB.                                                                                  |

<Warning>
  Imports are **all-or-nothing**. If the file has a structural problem, or any
  row fails validation, **nothing is imported**. Iru returns a report of every
  problem so you can fix the file and upload again.
</Warning>

## Columns

The import dialog lists every column your file can include: required core columns,
optional core columns, and any custom attribute columns from your
[schema](/en/identity/directory/schema). Use the sample from **Download sample.csv**
as your header template. When your column names match the sample, each row's values
populate the matching profile fields on the user identified by `externalId`.

### Required

Every row must include a non-empty value for each of these columns.

| Column       | Description                                                           | Rules                                                                               | Example                |
| ------------ | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------- |
| `externalId` | Your stable identifier for the user; used to match rows on re-upload. | Non-empty; **unique within the file**.                                              | `EMP-10482`            |
| `username`   | The user's login username.                                            | Non-empty.                                                                          | `jane.doe`             |
| `email`      | The user's primary email address.                                     | A valid, **bare** address (no display name; `Jane <jane@example.com>` is rejected). | `jane.doe@example.com` |
| `firstName`  | Given name.                                                           | Non-empty.                                                                          | `Jane`                 |
| `lastName`   | Family name.                                                          | Non-empty.                                                                          | `Doe`                  |

### Optional

Leave a cell blank, or omit the column entirely, when a value is not needed. If
`domain` is blank, Iru derives it from the email address.

| Column          | Description                 | Rules                                                                                    | Example         |
| --------------- | --------------------------- | ---------------------------------------------------------------------------------------- | --------------- |
| `domain`        | The user's domain.          | If blank, derived from the part of `email` after `@`. Set it only when it should differ. | `example.com`   |
| `streetAddress` | Street line of the address. | Free text.                                                                               | `123 Main St`   |
| `locality`      | City or town.               | Free text.                                                                               | `San Francisco` |
| `region`        | State, province, or region. | Free text.                                                                               | `CA`            |
| `postalCode`    | Postal or ZIP code.         | Free text.                                                                               | `94105`         |
| `country`       | Country.                    | Free text (for example an ISO country code).                                             | `US`            |
| `phone`         | Phone number.               | Normalized to E.164; see [Phone numbers](#phone-numbers).                                | `+14155550101`  |

### Custom attributes

After you define custom attributes in your schema, the import dialog lists them
under **Custom attribute columns**, named `categorySlug.attributeSlug` from the
category slug and attribute slug. For example, `department` and `title` in a
category whose slug is `extrainfo` appear as `extrainfo.department` and
`extrainfo.title`.

Add one column per attribute to your CSV header. Core column names must match
exactly; custom attribute column names ignore capitalization and spacing.

### Sample file

In the import dialog, click **Download sample.csv** for a starter file with all
core columns and one example user. If your schema includes custom attributes,
click the arrow next to **Download sample.csv**, then choose **With custom
attributes**. That sample adds a column for each custom attribute in your schema
and leaves those cells blank in the example row so you can fill them in. The core
columns and example user stay the same.

Replace the example user with your own rows and keep the header names unchanged.

<Expandable title="Optional column variations">
  You do not need every optional column in every row. A row can include only the
  required columns, a partial address, or a phone number with or without a country
  code. Leave `domain` blank when it should match the email address, and leave
  custom attribute cells blank when you have no value for a user.
</Expandable>

## Field behavior

### Domain derivation

* If `domain` is **blank**, Iru takes it from the email address (`jane.doe@example.com`
  becomes `example.com`).
* If `domain` is **provided**, that value is used as-is.
* A domain that does not already exist is created automatically.

### Phone numbers

Phone values are normalized to **E.164** (a leading `+`, country code, then digits,
with no spaces or punctuation), the same way phone numbers are stored everywhere
else in Iru Identity.

| You enter         | Stored as       | Why                                                                  |
| ----------------- | --------------- | -------------------------------------------------------------------- |
| `4155550101`      | `+14155550101`  | No country code; the default **`+1`** (North America) is added.      |
| `(415) 555-0101`  | `+14155550101`  | Separators removed, `+1` added.                                      |
| `14155550101`     | `+14155550101`  | Recognized as a North American number that already includes the `1`. |
| `+14155550101`    | `+14155550101`  | Already E.164; kept as-is.                                           |
| `+44 7911 123456` | `+447911123456` | International (`+` present); country code kept, separators removed.  |
| `1-800-FLOWERS`   | *rejected*      | Contains letters.                                                    |
| `555-0101`        | *rejected*      | Too few digits to be a valid number.                                 |

<Tip>
  Numbers outside North America must include a `+` and their country code (for
  example `+44…`). Without a leading `+`, a number is assumed to be a 10-digit
  North American number and gets `+1`.
</Tip>

### Addresses

Iru stores an address whenever **any** address column has a value. A partial
address (only `locality` and `country`, for example) is fine; missing parts stay
blank. If every address column is blank, no address is stored.

## Run the import

The **Import users via CSV** dialog shows the required and optional core columns,
any custom attribute columns from your schema, and **Download sample.csv** with an
arrow for **With custom attributes**. Drag a file onto the upload area or browse
to select one.

<Steps>
  <Step title="Start the import">
    In **Directory → Users**, click the arrow next to **+ User**, then choose
    **Import via CSV**. Review the column list in the dialog against the tables
    above.
  </Step>

  <Step title="Prepare your file">
    Click **Download sample.csv** for a file with core columns and one example
    user. If your file includes custom attribute columns, click the arrow next to
    **Download sample.csv**, then choose **With custom attributes**. Replace the
    example user with your own rows and keep the sample header names so each row
    maps to the right user and profile fields.
  </Step>

  <Step title="Choose your file">
    Select your CSV or drag it onto the upload area.
  </Step>

  <Step title="Upload and validate">
    Iru validates the whole file first. If validation passes, rows are accepted and
    users are created or updated in the background. If validation fails, the
    dialog shows a report and nothing is imported.
  </Step>

  <Step title="Review the result">
    A successful upload reports how many users were staged and adds the import to
    your history, where you can review the outcome for each row.
  </Step>
</Steps>

After you upload, Iru validates the entire file. If validation fails, nothing is
imported. Fix the reported problems and upload again. When validation passes, rows
are accepted and staged, and users are created or updated in the background. Check
the import in your history to confirm each row. A row can still fail at apply time
after the file is accepted; correct those rows and re-import them. See
[Validation and errors](#validation-and-errors) for the two stages of checks.

## Create vs. update: re-uploading

Users are matched on `externalId` within the `csv` source:

* A **new `externalId`** creates a new user with the **`end-user`** role.
* An **existing `externalId`** updates that user's profile from the row. Role and
  group memberships do not change.

Re-upload a corrected or expanded CSV at any time to keep imported users in sync.
Every `externalId` in a file must be unique.

<Note>
  Re-upload the **complete** current file each time, not just the rows you
  changed. Each import is matched on `externalId`.
</Note>

## Validation and errors

Validation runs in two stages. Until the file is fully accepted, **nothing is
imported**.

<AccordionGroup>
  <Accordion title="File problems (the whole file is rejected)">
    The file cannot be processed. Common causes include a duplicate column, a
    missing required column, an empty file, a header with no data rows, or more
    rows than the limit. Fix the file and upload again.
  </Accordion>

  <Accordion title="Row problems (still nothing imported)">
    The file is well-formed, but one or more rows are invalid. Iru returns every
    problem with the **line** (the header is line 1, so the first data row is
    line 2), the **column** at fault (blank for whole-row issues), and a
    **message**. For example:

    | Line | Column       | Message                      |
    | ---- | ------------ | ---------------------------- |
    | 3    | `email`      | is not a valid email address |
    | 5    | `username`   | is required                  |
    | 8    | `phone`      | is not a valid phone number  |
    | 9    | `externalId` | is duplicated in this file   |

    Fix the listed rows and re-upload.
  </Accordion>

  <Accordion title="Apply errors (after a clean file is accepted)">
    After a file is accepted, rows are applied in the background. A row can still
    fail at this stage if, for example, the email is already in use by another
    user or a value conflicts with a
    [unique attribute](/en/identity/directory/schema). These **apply errors** are
    recorded on the import batch with the row's `externalId`, `username`, and error
    details. Correct those rows and re-import them.
  </Accordion>
</AccordionGroup>

## When to use HR sync instead

Re-upload a CSV whenever your list changes and Iru re-checks every row. For
continuous, hands-off provisioning, connect your HR system as the authoritative
record instead.

|                     | CSV import                      | HR system sync                         |
| ------------------- | ------------------------------- | -------------------------------------- |
| **Trigger**         | You upload a file               | Runs automatically on a schedule       |
| **Keeps current?**  | When you re-upload              | Yes (re-syncs automatically)           |
| **Create / update** | Both, matched on `externalId`   | Both, automatically                    |
| **Removal**         | Manual                          | Can remove users as they're offboarded |
| **Best for**        | Initial loads, ad-hoc additions | Ongoing lifecycle automation           |

<Note>
  You can use both. Setup for HR sync is covered in
  [Directory Sync](/en/identity/directory/directory-sync).
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Connect your HR system" icon="building-user" href="/en/identity/directory/directory-sync">
    Automate creates, updates, and removals on a schedule.
  </Card>

  <Card title="Design your schema first" icon="table-list" href="/en/identity/directory/schema">
    Define required and unique attributes before you import.
  </Card>

  <Card title="Manage individual users" icon="user" href="/en/identity/directory/users">
    Edit, suspend, and invite the users you've imported.
  </Card>

  <Card title="Organize into groups" icon="users" href="/en/identity/directory/groups">
    Turn your imported users into assignable groups.
  </Card>
</CardGroup>
