> For the complete documentation index, see [llms.txt](https://docs.monolithforensics.com/monolith/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.monolithforensics.com/monolith/using-monolith/data-import/evidence-importer.md).

# Evidence Importer

This guide explains how to use the Evidence CSV importer in Monolith, including how lookup columns are resolved and how each row is validated before import.

## What the importer does

The importer takes a CSV file, maps your CSV columns to Monolith evidence fields, resolves values (for example, user names to user IDs), validates each row, and then imports only the rows you choose.

## Import workflow (4 steps)

{% stepper %}
{% step %}

### Upload

Upload one `.csv` file (default max size: 10 MB).
{% endstep %}

{% step %}

### Map Columns

Map each Monolith field to a CSV column. Required fields must be mapped to continue.
{% endstep %}

{% step %}

### Preview

Monolith resolves and validates every row, then marks rows as:

* **Valid** (green check)
* **Warning** (yellow alert)
* **Error** (red X)
  {% endstep %}

{% step %}

### Import

Import runs in batches (default: 5 records per batch). You will see a final success/failure summary.
{% endstep %}
{% endstepper %}

## Auto-mapping behavior

When your CSV is uploaded, Monolith tries to map columns automatically in this order:

1. Exact match to the target field key
2. Exact match to the target field label
3. Exact match to any configured alias

All matching is case-insensitive and trimmed. A CSV column is only used once during auto-map.

### Auto-mapping aliases by column

These are the aliases used for built-in Evidence fields during step 3 of auto-mapping.

| Monolith field                        | Aliases checked                                                         |
| ------------------------------------- | ----------------------------------------------------------------------- |
| `case_id` (Case Number)               | `case id`, `case`, `case number`                                        |
| `evidence_number` (Evidence Number)   | `evidence_number`, `item_number`, `evidence #`, `evi #`                 |
| `item_type` (Evidence Type)           | `type`, `evidence_type`, `evi_type`, `item type`                        |
| `manufacturer` (Brand / Manufacturer) | `brand`, `make`, `item brand`, `provider`                               |
| `model_name` (Item Name)              | `name`, `item name`, `device name`, `model`                             |
| `model_number` (Model Number)         | `model_number`, `model #`, `model`                                      |
| `serial_number` (Unique Identifier)   | `serial`, `serial number`, `serial #`, `unique id`, `unique identifier` |
| `capacity` (Size)                     | `size`, `storage`, `capacity`                                           |
| `capacity_unit` (Size Unit)           | `size unit`, `unit`, `capacity unit`, `size`, `capacity`                |
| `assigned_user_id` (Assigned User)    | `assigned user`, `assigned to`, `assigned to user`                      |
| `location_id` (Location)              | `location`, `evidence location`, `current location`                     |
| `priority_id` (Priority)              | `priority`, `evidence priority`                                         |
| `description` (Description)           | `description`, `notes`, `details`                                       |

For custom evidence fields, the importer sets one alias automatically: the custom field name lowercased.

## Lookup columns: how they work

Lookup columns are fields where the importer converts user-friendly CSV text into the internal Monolith value (usually an ID or canonical option).

If a lookup value cannot be resolved, the importer sets it to `null` and adds a **warning** (not an error), unless that field is required.

### Built-in evidence lookups

* **Case Number (`case_id`)**
  * If importing from within a case, the case is fixed and injected automatically.
  * Otherwise, the value is treated as case number text and looked up in Monolith.
  * An error is shown if case number cannot be found.
* **Evidence Type (`item_type`)**
  * Matches CSV text to an existing evidence type by name (case-insensitive).
  * The type is set to null if a match is not found.
* **Assigned User (`assigned_user_id`)**
  * Accepts any of:
    * Numeric user ID
    * User email
    * Full name
  * Resolves to the user ID.
  * Assigned user is not applied if there is no user match.
* **Location (`location_id`)**
  * Matches by exact location name (case-insensitive).
  * If multiple locations share the same name, row gets an **error**: duplicate location match.
* **Priority (`priority_id`)**
  * Matches by priority name (case-insensitive) and resolves to priority ID.
* **Size + Size Unit (`capacity`, `capacity_unit`)**
  * You can provide a combined value like `256 GB`.
  * Importer extracts:
    * `capacity = 256`
    * `capacity_unit = GB`
  * Unit matching supports `KB`, `MB`, `GB`, `TB`.

### Custom field lookups

Custom evidence fields are added dynamically and use importer logic based on field type:

* **Drop-down custom fields**
  * CSV value must match one of the configured options.
  * Value is null if there is no match to options.
* **Tag-box custom fields**
  * Supports multi-value input split by:
    * comma `,`
    * semicolon `;`
    * pipe `|`
    * newline
  * Values are trimmed and deduplicated.
  * Known options are normalized to configured option values.
  * Unknown values are ignored.
* **Date-box custom fields**
  * Accepts:
    * Parseable date strings (for example `2026-03-01`, `3/1/2026`)
    * Unix timestamp values (seconds or milliseconds)
  * Normalized to `YYYY-MM-DD`.

## Row validation: what is checked

Every row is validated after mapping and value resolution.

{% stepper %}
{% step %}

### 1) Required field checks

For each required field:

* If no CSV column was mapped: error\
  `"<Field> is required but no column was mapped"`
* If mapped but row value is empty: error\
  `"<Field> is required"`

Empty means `null`, `undefined`, empty string, or empty array.
{% endstep %}

{% step %}

### 2) Type checks

* **Number fields**
  * Must resolve to a number.
  * Non-numeric text produces an error.
* **Boolean fields** (if present in a definition)
  * Accepts normalized boolean values (`true/false`, `yes/no`, `1/0` during coercion).
  * Non-boolean result is an error.
* **Select fields**
  * Value must match one of the valid options.
  * For multi-select arrays (for example tag-box), every item must be valid.
* **Date fields**
  * Must be parseable as a valid date string.
    {% endstep %}

{% step %}

### 3) Custom field-level validation

Some fields run extra custom checks. For Evidence import, notable example:

* **Location**: fails if the same location name maps to multiple location records.
  {% endstep %}

{% step %}

### 4) Resolver warning checks

If a field has a resolver, the CSV column is mapped, and the source row had data, but the resolver still returns `null`, the row gets a warning:

`"Could not resolve value for <Field>"`

This lets users proceed while seeing unresolved lookups.
{% endstep %}
{% endstepper %}

## Errors vs warnings

* **Errors**
  * Row is invalid.
  * Invalid rows are not auto-selected for import.
* **Warnings**
  * Row can still be valid/importable.
  * Warnings flag possible data quality issues (for example unresolved lookup).

## Row selection behavior in Preview

* Valid rows are auto-selected by default.
* You can:
  * **Select Valid**
  * **Select All**
  * **Deselect All**
* Import button shows how many selected rows will be imported.

## Import execution and reporting

* Import runs only for selected **valid** rows.
* Processing occurs in batches (default size: 5).
* Final screen reports:
  * successful imports
  * failed imports
  * row-level error messages (if any)

## CSV preparation tips

* Use stable, specific values for lookups:
  * unique case numbers
  * exact evidence type names
  * exact location names
  * user email/full name for assigned user
* Avoid ambiguous location names that exist more than once.
* For size columns, prefer `capacity` + `capacity_unit`, or a single value like `512 GB`.
* For tag-box custom fields, separate values with commas/semicolons/pipes/new lines.
* Validate date formats before upload when possible.
